Generado con IA · Tema 2

Elementos funcionales de un ordenador digital.

Informática Comunidad Valenciana 11.157 palabras
Descargar:
Este tema lo ha redactado la IA de OposicionesIA, sin retoques. Es una muestra de la calidad y el formato que obtienes al generar tus propios temas dentro de la plataforma.

Tema 2. Elementos funcionales de un ordenador digital

Índice

  1. Introducción
  2. Concepto de ordenador digital y modelos teóricos de computación
  3. La máquina de Turing como fundamento teórico
  4. El modelo de von Neumann frente a la arquitectura Harvard
  5. La Unidad Central de Proceso (CPU): estructura y funcionamiento
  6. El ciclo de instrucción: búsqueda, decodificación y ejecución
  7. El subsistema de memoria y su jerarquía
  8. El subsistema de entrada/salida (E/S)
  9. El sistema de interconexión: buses de datos, direcciones y control
  10. Generaciones y evolución de los ordenadores digitales
  11. Aplicación didáctica y relación con el currículo
  12. Conclusión
  13. Bibliografía y referencias

1. Introducción

El ordenador digital constituye el artefacto tecnológico más versátil que ha producido la ingeniería, y su estudio resulta imprescindible para cualquier docente de la especialidad de Informática. Comprender qué es un ordenador no se limita a enumerar las piezas que lo componen, sino que exige entender la articulación funcional de esas piezas en torno a una idea central: la máquina de programa almacenado. Un ordenador no es una calculadora rápida ni un conjunto de circuitos sueltos, sino la materialización física de un modelo abstracto de computación capaz de ejecutar cualquier algoritmo expresable, dentro de los límites de su memoria y su tiempo.

El propósito de este tema es analizar los elementos funcionales de un ordenador digital, esto es, las unidades que, mediante su cooperación, hacen posible el procesamiento automático de la información. Para abordarlo con el rigor que exige una prueba de oposición, conviene partir de los fundamentos teóricos —la máquina de Turing, que define qué significa computar— para descender después a la arquitectura concreta —el modelo de von Neumann y su alternativa, la arquitectura Harvard— y, finalmente, describir los subsistemas físicos: la unidad central de proceso, la memoria, los dispositivos de entrada/salida y el sistema de buses que los interconecta. Cerraremos con la perspectiva histórica de las generaciones y con la inserción de estos conceptos en el currículo de la Formación Profesional y de la Educación Secundaria.

El enfoque será deliberadamente integrador: cada elemento se explicará no de forma aislada, sino como parte de un sistema cuyo comportamiento global emerge de la interacción entre componentes. Esta visión sistémica es, además, la que mejor traslada al alumnado la naturaleza profunda de la computación y la que conviene cultivar en el aula. Conviene anticipar, asimismo, una tesis que vertebra todo el tema: los ordenadores han cambiado de tecnología varias veces —del relé a la válvula, de la válvula al transistor, del transistor al circuito integrado nanométrico—, pero los elementos funcionales y su lógica de cooperación se han mantenido como invariantes conceptuales. Distinguir lo que es contingente (la implementación) de lo que es estructural (la función) es precisamente la marca de una exposición de nivel de tribunal.

2. Concepto de ordenador digital y modelos teóricos de computación

Un ordenador digital es una máquina automática capaz de aceptar datos de entrada, procesarlos siguiendo un conjunto de instrucciones almacenadas (el programa) y producir resultados de salida, operando sobre información representada de forma discreta, normalmente mediante el sistema binario. El adjetivo «digital» se opone a «analógico»: mientras un computador analógico representa magnitudes mediante variables físicas continuas (tensiones, ángulos, presiones), el digital representa la información mediante dígitos, lo que le confiere precisión arbitraria, inmunidad relativa al ruido y, sobre todo, la capacidad de aplicar la teoría matemática del álgebra de Boole a su diseño.

La elección del sistema binario, con solo dos niveles lógicos, no es caprichosa. Un sistema de dos estados (típicamente dos umbrales de tensión) maximiza el margen de ruido: para confundir un 0 con un 1 hace falta una perturbación que cruce todo el intervalo de guarda entre ambos niveles, mientras que un sistema decimal con diez niveles necesitaría discriminar franjas de tensión mucho más estrechas y sería incomparablemente más frágil. A esa robustez física se suma la regeneración de la señal: cada puerta lógica recibe niveles degradados y emite niveles limpios, de modo que el error no se acumula a lo largo de una cadena de miles de puertas. Es esta combinación de robustez y capacidad de regeneración la que hace posible que un procesador con decenas de miles de millones de transistores conmutando miles de millones de veces por segundo funcione durante años sin errar un solo bit de forma perceptible. La teoría de la información de Claude Shannon (1948) y su formulación de la capacidad de canal dan el marco cuantitativo de por qué lo discreto vence a lo continuo en presencia de ruido.

Tres propiedades caracterizan al ordenador digital moderno y lo distinguen de máquinas anteriores:

  • Automatismo: una vez iniciada la ejecución, el ordenador procede sin intervención humana, gobernado por su programa.
  • Universalidad o programabilidad: la misma máquina física puede resolver problemas distintos sin más que cambiar el programa. Esta propiedad, hoy obvia, fue la gran ruptura conceptual frente a las máquinas de propósito fijo.
  • Programa almacenado: el programa reside en la memoria con el mismo estatus que los datos, lo que permite que un programa modifique a otro, se compile, se interprete o se cargue dinámicamente.

Estas propiedades no surgieron del ensayo ingenieril, sino de una reflexión teórica previa. Antes de que existiera el primer ordenador electrónico, los matemáticos ya habían formalizado qué significa calcular. En la década de 1930, en el contexto del programa de Hilbert y de la pregunta sobre la decidibilidad (el Entscheidungsproblem), se propusieron varios modelos formales de computación: la máquina de Turing de Alan Turing, el cálculo lambda de Alonzo Church y las funciones recursivas de Gödel, Kleene y Herbrand. La demostración de que los tres modelos son equivalentes —definen exactamente la misma clase de funciones computables— condujo a la tesis de Church-Turing: toda función intuitivamente calculable mediante un procedimiento mecánico es computable por una máquina de Turing. Conviene subrayar que la tesis no es un teorema demostrable, pues relaciona una noción intuitiva («calculable mediante un procedimiento mecánico») con una noción formal; es, más bien, una definición ampliamente aceptada porque todo modelo de cómputo razonable propuesto en noventa años ha resultado equivalente o más débil.

Esta tesis es la piedra angular de la informática teórica y tiene una consecuencia práctica de primer orden: cualquier ordenador real, por sofisticado que sea, no puede calcular nada que una máquina de Turing no pueda calcular. Las diferencias entre máquinas son de eficiencia (tiempo, memoria, consumo), no de poder de cómputo. Por eso el estudio de la máquina de Turing no es una curiosidad histórica, sino el marco que define los límites teóricos de todo elemento funcional que describiremos.

Merece la pena introducir aquí una observación de gran calado conceptual, que distingue una exposición notable de una excelente. El conjunto de todos los programas posibles es numerable (infinito, pero contable, pues cada programa es una cadena finita de símbolos), mientras que el conjunto de todas las funciones de los naturales en los naturales tiene la cardinalidad del continuo, estrictamente mayor. Por un sencillo argumento de cardinalidad se concluye que casi todas las funciones son incomputables: no existe ni podrá existir programa alguno que las calcule. Lo verdaderamente notable no es que haya problemas irresolubles, sino que la inmensa mayoría lo sea y que, aun así, casi todos los problemas que nos interesan en la práctica caigan del lado computable. Sobre esa clase de lo computable, la teoría de la complejidad superpone después una jerarquía de dificultad práctica —clases como P, NP, PSPACE— que clasifica los problemas resolubles según los recursos de tiempo y espacio que exigen, y que es materia de temas posteriores del temario.

3. La máquina de Turing como fundamento teórico

La máquina de Turing, descrita por Alan Turing en 1936 en su artículo «On Computable Numbers, with an Application to the Entscheidungsproblem», es un modelo matemático abstracto que captura la esencia del cálculo mecánico. No es un dispositivo físico, sino una definición formal cuya simplicidad la hace especialmente valiosa para razonar sobre lo computable.

3.1. Definición formal y componentes

Una máquina de Turing se define como una séptupla M = (Q, \Sigma, \Gamma, \delta, q_0, b, F), donde Q es un conjunto finito de estados, \Gamma es el alfabeto de cinta (los símbolos que pueden escribirse), \Sigma \subseteq \Gamma es el alfabeto de entrada, q_0 \in Q es el estado inicial, b \in \Gamma es el símbolo blanco, F \subseteq Q es el conjunto de estados finales o de aceptación, y \delta es la función de transición que constituye el «programa» de la máquina. En su versión determinista, la función de transición tiene la forma

\delta : Q \times \Gamma \longrightarrow Q \times \Gamma \times \{L, R\},

es decir, dado el estado actual y el símbolo leído, produce un nuevo estado, un símbolo a escribir y una dirección de movimiento (izquierda o derecha). Físicamente, el modelo se describe con tres elementos intuitivos:

  • Una cinta infinita dividida en celdas, cada una de las cuales contiene un símbolo del alfabeto. La cinta hace las veces de memoria y de dispositivo de entrada y salida simultáneamente.
  • Un cabezal de lectura/escritura que se posiciona sobre una celda, puede leer su símbolo, escribir uno nuevo y desplazarse una posición a la izquierda o a la derecha.
  • Una unidad de control que, en función del estado actual y del símbolo leído, decide qué símbolo escribir, en qué dirección moverse y a qué estado transitar. Es el embrión de la futura Unidad de Control de la CPU.

El funcionamiento procede por pasos discretos. En cada paso, la función de transición consulta el par (estado, símbolo leído) y determina la terna (símbolo a escribir, dirección de movimiento, nuevo estado). El cómputo termina cuando se alcanza un estado final o cuando no hay transición definida. La situación completa de la máquina en un instante —contenido de la cinta, posición del cabezal y estado actual— se denomina configuración o descripción instantánea, y la sucesión de configuraciones constituye el cómputo.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 440" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <rect x="0" y="0" width="760" height="440" fill="#ffffff"/> <!-- Título --> <text x="380" y="30" font-size="17" font-weight="bold" fill="#0f172a" text-anchor="middle">Máquina de Turing (modelo abstracto del cómputo)</text> <line x1="40" y1="40" x2="720" y2="40" stroke="#e2e8f0" stroke-width="1"/> <!-- ============ CINTA (arriba) ============ --> <text x="110" y="60" font-size="11" fill="#64748b">cinta infinita</text> <text x="650" y="60" font-size="11" fill="#64748b" text-anchor="end">cinta infinita</text> <!-- celda 1: ... --> <rect x="110" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="140" y="107" font-size="15" fill="#64748b" text-anchor="middle">...</text> <!-- celda 2: 1 --> <rect x="170" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="200" y="109" font-size="16" fill="#0f172a" text-anchor="middle">1</text> <!-- celda 3: 0 --> <rect x="230" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="260" y="109" font-size="16" fill="#0f172a" text-anchor="middle">0</text> <!-- celda 4: 1 --> <rect x="290" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="320" y="109" font-size="16" fill="#0f172a" text-anchor="middle">1</text> <!-- celda 5: 1 (activa, resaltada) --> <rect x="350" y="70" width="60" height="60" fill="#fef3c7" stroke="#ea580c" stroke-width="2"/> <text x="380" y="109" font-size="16" font-weight="bold" fill="#0f172a" text-anchor="middle">1</text> <!-- celda 6: 0 --> <rect x="410" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="440" y="109" font-size="16" fill="#0f172a" text-anchor="middle">0</text> <!-- celda 7: 1 --> <rect x="470" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="500" y="109" font-size="16" fill="#0f172a" text-anchor="middle">1</text> <!-- celda 8: B (blanco) --> <rect x="530" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="560" y="102" font-size="15" fill="#0f172a" text-anchor="middle">B</text> <text x="560" y="120" font-size="9.5" fill="#64748b" text-anchor="middle">(blanco)</text> <!-- celda 9: ... --> <rect x="590" y="70" width="60" height="60" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="620" y="107" font-size="15" fill="#64748b" text-anchor="middle">...</text> <!-- ============ CABEZAL L/E (triángulo bajo la celda activa) ============ --> <polygon points="380,136 372,150 388,150" fill="#ea580c"/> <polygon points="362,180 398,180 386,152 374,152" fill="#fef3c7" stroke="#ea580c" stroke-width="1.5"/> <!-- etiqueta a la DERECHA del triángulo, con clearance --> <text x="410" y="170" font-size="12.5" font-weight="bold" fill="#0f172a">cabezal L/E</text> <!-- ============ CONEXIÓN Unidad de control -> cabezal (ortogonal, pasillo libre) ============ --> <line x1="140" y1="284" x2="140" y2="224" stroke="#2563eb" stroke-width="1.8"/> <line x1="140" y1="224" x2="380" y2="224" stroke="#2563eb" stroke-width="1.8"/> <line x1="380" y1="224" x2="380" y2="192" stroke="#2563eb" stroke-width="1.8"/> <polygon points="380,180 374,192 386,192" fill="#2563eb"/> <!-- etiqueta paralela al tramo horizontal, por debajo --> <text x="160" y="244" font-size="11" fill="#2563eb">control de posición y estado</text> <!-- ============ UNIDAD DE CONTROL (abajo-izquierda) ============ --> <rect x="40" y="284" width="200" height="64" fill="#eff6ff" stroke="#2563eb" stroke-width="1.5" rx="6"/> <text x="140" y="312" font-size="14" font-weight="bold" fill="#0f172a" text-anchor="middle">Unidad de control</text> <text x="140" y="332" font-size="13" fill="#334155" text-anchor="middle">(estado q)</text> <!-- ============ FUNCIÓN DE TRANSICIÓN (a la derecha, unión horizontal) ============ --> <rect x="420" y="276" width="300" height="78" fill="#ecfdf5" stroke="#059669" stroke-width="1.5" rx="6"/> <text x="570" y="298" font-size="13.5" font-weight="bold" fill="#0f172a" text-anchor="middle">Función de transición</text> <text x="570" y="319" font-size="12" fill="#334155" text-anchor="middle">δ(estado, símbolo leído) →</text> <text x="570" y="338" font-size="12" fill="#334155" text-anchor="middle">(escribir, mover Izq/Der, nuevo estado)</text> <!-- enlace horizontal bidireccional entre las dos cajas --> <line x1="240" y1="315" x2="420" y2="315" stroke="#334155" stroke-width="1.5"/> <polygon points="420,315 408,310 408,320" fill="#334155"/> <polygon points="240,315 252,310 252,320" fill="#334155"/> <!-- Pie --> <text x="380" y="420" font-size="12.5" font-style="italic" fill="#64748b" text-anchor="middle">La cinta actúa como memoria y como dispositivo de E/S a la vez.</text> </svg>

Figura 1. Máquina de Turing: cinta infinita, cabezal de lectura/escritura, unidad de control y función de transición.

Conviene fijar estas ideas con un ejemplo concreto y resoluble a mano, del tipo que un tribunal agradece porque demuestra dominio real y no memorístico. Diseñemos una máquina que calcula el sucesor de un número binario, es decir, que le suma una unidad. Situamos el número en la cinta con el bit menos significativo a la derecha y colocamos el cabezal sobre su primer dígito. La máquina tiene tres estados —q_0 (avanzar hasta el extremo derecho), q_1 (propagar el acarreo hacia la izquierda) y q_f (aceptación)— y su función de transición se resume en la siguiente tabla:

Estado Lee 0 Lee 1 Lee _ (blanco)
q_0 0, R, q_0 1, R, q_0 _, L, q_1
q_1 1, –, q_f 0, L, q_1 1, –, q_f

La lectura es directa: en q_0 la máquina simplemente recorre todo el número hacia la derecha sin alterarlo hasta topar con el blanco, momento en que retrocede una casilla y pasa a q_1. En q_1 ejecuta la suma con acarreo: cada 1 que encuentra se convierte en 0 y el acarreo continúa hacia la izquierda; en cuanto encuentra un 0 (o el blanco del extremo izquierdo) escribe un 1, el acarreo se absorbe y la máquina se detiene. Sigamos el cómputo para la entrada 1011 (que es 11 en decimal): tras recorrerla en q_0, en q_1 se lee el 1 final y se escribe 0 (queda 1010), se avanza a la izquierda y se lee otro 1, que se convierte en 0 (queda 1000), y al leer el 0 se escribe 1 y se acepta, resultando 1100, que es 12 en decimal. La suma se ha realizado por propagación de acarreo, exactamente como lo hará un sumador hardware real. Este pequeño ejemplo ilustra cómo un comportamiento aritmético emerge de reglas locales elementales, que es la esencia de toda computación.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 780 620" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <text x="390" y="32" text-anchor="middle" font-size="16" font-weight="bold" fill="#0f172a">Máquina de Turing: sucesor de un número binario</text> <line x1="372" y1="78" x2="372" y2="448" stroke="#e2e8f0" stroke-width="1.5"/> <text x="180" y="62" text-anchor="middle" font-size="13" font-weight="bold" fill="#334155">Diagrama de estados</text> <text x="180" y="76" text-anchor="middle" font-size="10" font-style="italic" fill="#64748b">inicio</text> <line x1="180" y1="84" x2="180" y2="91" stroke="#334155" stroke-width="1.6"/> <polygon points="180,100 173,90 187,90" fill="#334155"/> <circle cx="180" cy="130" r="30" fill="#f8fafc" stroke="#0f172a" stroke-width="1.8"/> <text x="180" y="135" text-anchor="middle" font-size="13" font-weight="bold" fill="#0f172a">q0</text> <path d="M154,112 C112,96 108,166 154,148" fill="none" stroke="#334155" stroke-width="1.6"/> <polygon points="155,148 146,150 151,140" fill="#334155"/> <text x="100" y="123" text-anchor="end" font-size="11" fill="#0f172a">0 / 0 , R</text> <text x="100" y="142" text-anchor="end" font-size="11" fill="#0f172a">1 / 1 , R</text> <line x1="180" y1="160" x2="180" y2="231" stroke="#334155" stroke-width="1.6"/> <polygon points="180,240 173,230 187,230" fill="#334155"/> <text x="196" y="203" text-anchor="start" font-size="11" fill="#0f172a">_ / _ , L</text> <circle cx="180" cy="270" r="30" fill="#f8fafc" stroke="#0f172a" stroke-width="1.8"/> <text x="180" y="275" text-anchor="middle" font-size="13" font-weight="bold" fill="#0f172a">q1</text> <path d="M154,252 C112,236 108,306 154,288" fill="none" stroke="#334155" stroke-width="1.6"/> <polygon points="155,288 146,290 151,280" fill="#334155"/> <text x="100" y="274" text-anchor="end" font-size="11" fill="#0f172a">1 / 0 , L</text> <line x1="180" y1="300" x2="180" y2="367" stroke="#334155" stroke-width="1.6"/> <polygon points="180,376 173,366 187,366" fill="#334155"/> <text x="196" y="329" text-anchor="start" font-size="11" fill="#0f172a">0 / 1 , –</text> <text x="196" y="351" text-anchor="start" font-size="11" fill="#0f172a">_ / 1 , –</text> <circle cx="180" cy="410" r="34" fill="#ecfdf5" stroke="#059669" stroke-width="1.8"/> <circle cx="180" cy="410" r="28" fill="none" stroke="#059669" stroke-width="1.8"/> <text x="180" y="415" text-anchor="middle" font-size="13" font-weight="bold" fill="#0f172a">qf</text> <text x="570" y="62" text-anchor="middle" font-size="13" font-weight="bold" fill="#334155">Tabla de transición</text> <rect x="400" y="90" width="340" height="40" fill="#eff6ff"/> <rect x="400" y="130" width="70" height="80" fill="#eff6ff"/> <rect x="400" y="90" width="340" height="120" fill="none" stroke="#334155" stroke-width="1.4"/> <line x1="470" y1="90" x2="470" y2="210" stroke="#334155" stroke-width="1"/> <line x1="560" y1="90" x2="560" y2="210" stroke="#334155" stroke-width="1"/> <line x1="650" y1="90" x2="650" y2="210" stroke="#334155" stroke-width="1"/> <line x1="400" y1="130" x2="740" y2="130" stroke="#334155" stroke-width="1"/> <line x1="400" y1="170" x2="740" y2="170" stroke="#334155" stroke-width="1"/> <text x="435" y="115" text-anchor="middle" font-size="13" font-style="italic" font-weight="bold" fill="#334155">δ</text> <text x="515" y="115" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">Lee 0</text> <text x="605" y="115" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">Lee 1</text> <text x="695" y="115" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">Lee _</text> <text x="435" y="155" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">q0</text> <text x="515" y="155" text-anchor="middle" font-size="12" fill="#0f172a">0,R,q0</text> <text x="605" y="155" text-anchor="middle" font-size="12" fill="#0f172a">1,R,q0</text> <text x="695" y="155" text-anchor="middle" font-size="12" fill="#0f172a">_,L,q1</text> <text x="435" y="195" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">q1</text> <text x="515" y="195" text-anchor="middle" font-size="12" fill="#0f172a">1,–,qf</text> <text x="605" y="195" text-anchor="middle" font-size="12" fill="#0f172a">0,L,q1</text> <text x="695" y="195" text-anchor="middle" font-size="12" fill="#0f172a">1,–,qf</text> <text x="400" y="240" text-anchor="start" font-size="10.5" font-style="italic" fill="#64748b">_ representa el blanco de la cinta.</text> <text x="400" y="257" text-anchor="start" font-size="10.5" font-style="italic" fill="#64748b">– indica que el cabezal no se mueve.</text> <rect x="30" y="460" width="720" height="140" rx="10" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1.4"/> <text x="390" y="485" text-anchor="middle" font-size="13" font-weight="bold" fill="#334155">Traza de la cinta para la entrada 1011 (= 11)</text> <rect x="120" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="144" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="168" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="192" y="510" width="24" height="26" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="132" y="528" text-anchor="middle" font-size="12" fill="#0f172a">1</text> <text x="156" y="528" text-anchor="middle" font-size="12" fill="#0f172a">0</text> <text x="180" y="528" text-anchor="middle" font-size="12" fill="#0f172a">1</text> <text x="204" y="528" text-anchor="middle" font-size="12" font-weight="bold" fill="#2563eb">1</text> <line x1="194" y1="540" x2="214" y2="540" stroke="#2563eb" stroke-width="2.4"/> <text x="168" y="560" text-anchor="middle" font-size="10" font-style="italic" fill="#64748b">entrada</text> <line x1="224" y1="523" x2="262" y2="523" stroke="#334155" stroke-width="1.5"/> <polygon points="270,523 262,519 262,527" fill="#334155"/> <rect x="278" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="302" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="326" y="510" width="24" height="26" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <rect x="350" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <text x="290" y="528" text-anchor="middle" font-size="12" fill="#0f172a">1</text> <text x="314" y="528" text-anchor="middle" font-size="12" fill="#0f172a">0</text> <text x="338" y="528" text-anchor="middle" font-size="12" font-weight="bold" fill="#2563eb">1</text> <text x="362" y="528" text-anchor="middle" font-size="12" fill="#0f172a">0</text> <line x1="328" y1="540" x2="348" y2="540" stroke="#2563eb" stroke-width="2.4"/> <line x1="382" y1="523" x2="420" y2="523" stroke="#334155" stroke-width="1.5"/> <polygon points="428,523 420,519 420,527" fill="#334155"/> <rect x="436" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="460" y="510" width="24" height="26" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <rect x="484" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="508" y="510" width="24" height="26" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <text x="448" y="528" text-anchor="middle" font-size="12" fill="#0f172a">1</text> <text x="472" y="528" text-anchor="middle" font-size="12" font-weight="bold" fill="#2563eb">0</text> <text x="496" y="528" text-anchor="middle" font-size="12" fill="#0f172a">0</text> <text x="520" y="528" text-anchor="middle" font-size="12" fill="#0f172a">0</text> <line x1="462" y1="540" x2="482" y2="540" stroke="#2563eb" stroke-width="2.4"/> <line x1="540" y1="523" x2="578" y2="523" stroke="#334155" stroke-width="1.5"/> <polygon points="586,523 578,519 578,527" fill="#334155"/> <rect x="594" y="510" width="24" height="26" fill="#ffffff" stroke="#059669" stroke-width="1.3"/> <rect x="618" y="510" width="24" height="26" fill="#ecfdf5" stroke="#059669" stroke-width="1.3"/> <rect x="642" y="510" width="24" height="26" fill="#ffffff" stroke="#059669" stroke-width="1.3"/> <rect x="666" y="510" width="24" height="26" fill="#ffffff" stroke="#059669" stroke-width="1.3"/> <text x="606" y="528" text-anchor="middle" font-size="12" fill="#0f172a">1</text> <text x="630" y="528" text-anchor="middle" font-size="12" font-weight="bold" fill="#059669">1</text> <text x="654" y="528" text-anchor="middle" font-size="12" fill="#0f172a">0</text> <text x="678" y="528" text-anchor="middle" font-size="12" fill="#0f172a">0</text> <line x1="620" y1="540" x2="640" y2="540" stroke="#059669" stroke-width="2.4"/> <text x="642" y="560" text-anchor="middle" font-size="10" font-style="italic" fill="#059669">resultado (= 12)</text> </svg>

Figura 2. Máquina de Turing que calcula el sucesor de un número binario: diagrama de estados y tabla de transición.

3.2. Variantes del modelo y robustez de la definición

Una pregunta natural es si la máquina de Turing es artificialmente débil o fuerte por las decisiones concretas de su definición. La respuesta, y es un punto que distingue a un buen opositor, es que el modelo es robusto: numerosas variantes que parecerían más potentes resultan ser exactamente equivalentes en poder de cómputo. Una máquina con varias cintas puede simularse con una sola (a costa de un factor cuadrático de tiempo); una máquina no determinista, en la que \delta ofrece varias transiciones posibles y la máquina «acierta» si alguna rama acepta, puede simularse con una determinista (a costa, en el peor caso, de un tiempo exponencial, cuestión que está en el corazón del problema P frente a NP); una cinta infinita solo por un extremo equivale a una infinita por ambos. Todas estas variantes reconocen exactamente la misma clase de lenguajes, los recursivamente enumerables, y calculan exactamente las mismas funciones. Esta invarianza es la que da solidez a la tesis de Church-Turing: no dependemos de los detalles del modelo. La tesis de Church-Turing extendida añade que las diferencias de eficiencia entre modelos razonables son a lo sumo polinómicas, afirmación que la computación cuántica pone en cuestión y que constituye una de las fronteras teóricas más activas.

3.3. La máquina universal de Turing

La aportación más profunda de Turing fue demostrar la existencia de una Máquina Universal de Turing: una única máquina U capaz de simular el comportamiento de cualquier otra máquina de Turing M, sin más que recibir en su cinta la descripción codificada de M —una cadena finita de símbolos que representa su tabla de transición— junto con la entrada de M. Formalmente, U(\langle M \rangle, w) = M(w). Esta idea —una máquina que lee la descripción de otra máquina y la ejecuta— es exactamente el germen conceptual del programa almacenado y, por tanto, del ordenador moderno. El procesador físico es, en esencia, una realización de la máquina universal: lee instrucciones codificadas en memoria (la «descripción» del programa) y las ejecuta sea cual sea el programa. La distinción entre hardware fijo (la máquina universal U, esto es, el procesador) y software variable (la descripción \langle M \rangle, esto es, el programa) nace aquí, en un artículo de matemática pura de 1936, una década antes del primer ordenador electrónico.

3.4. Implicaciones: lo computable y lo incomputable

La máquina de Turing no solo define qué se puede calcular, sino que delimita qué no se puede. Turing demostró que el problema de la parada (decidir, dado un programa y una entrada, si la ejecución terminará o se ejecutará indefinidamente) es indecidible: no existe ningún algoritmo general que lo resuelva. El argumento es una diagonalización: si existiera un decisor H que resolviera la parada, podríamos construir un programa D que, aplicado a sí mismo, se detuviera si y solo si no se detiene, contradicción. Este resultado no es una anécdota: el teorema de Rice lo generaliza afirmando que cualquier propiedad semántica no trivial de los programas (¿calcula esta función una función constante?, ¿produce siempre una salida válida?) es indecidible. De ahí que los verificadores de software, los detectores de bucles infinitos, los analizadores de código muerto o los antivirus perfectos sean teóricamente imposibles en su forma general, y que las herramientas reales se conformen con aproximaciones conservadoras. Conviene transmitir al alumnado que la informática nace con la conciencia de sus propios límites, lo que la dignifica como disciplina científica y no meramente técnica: es de las pocas ingenierías que conocen con precisión matemática la frontera de lo imposible.

4. El modelo de von Neumann frente a la arquitectura Harvard

Si la máquina de Turing define qué es computable, la arquitectura de von Neumann define cómo organizar físicamente un ordenador de propósito general. Descrita en 1945 en el célebre «First Draft of a Report on the EDVAC», atribuido a John von Neumann pero fruto del trabajo colectivo del equipo del ENIAC (Eckert, Mauchly y otros), esta arquitectura sigue siendo, ochenta años después, el patrón conceptual de la inmensa mayoría de los ordenadores.

4.1. Principios del modelo de von Neumann

El modelo se asienta sobre cuatro principios:

  1. Concepto de programa almacenado: las instrucciones del programa se guardan en la memoria principal, igual que los datos. No hay distinción física entre unas y otros; ambos son palabras binarias indistinguibles fuera de su contexto de uso.
  2. Memoria única y direccionable: un espacio de memoria lineal donde cada celda posee una dirección, accesible para lectura y escritura.
  3. Ejecución secuencial: las instrucciones se ejecutan una tras otra en el orden en que residen en memoria, salvo que una instrucción de salto altere el flujo. Un registro especial, el contador de programa, mantiene la dirección de la siguiente instrucción.
  4. Estructura en cuatro subsistemas: unidad de control, unidad aritmético-lógica, memoria y unidades de entrada/salida, interconectados.

El modelo introduce una simplificación de enorme valor: al tratar instrucciones y datos por igual, un programa puede generar o modificar otro programa, lo que habilita compiladores, ensambladores, sistemas operativos y la carga dinámica de código. Es la traducción ingenieril directa de la máquina universal de Turing. Ahora bien, esa misma indistinción entre código y datos tiene una cara oscura: es la raíz última de familias enteras de vulnerabilidades de seguridad, como los desbordamientos de búfer que inyectan datos y consiguen que la CPU los ejecute como código. Las contramedidas modernas —el bit de no ejecución (NX/XD), la aleatorización del espacio de direcciones (ASLR) o las claves de autenticación de punteros— pueden entenderse como reintroducciones parciales y selectivas de la separación Harvard en un mundo von Neumann, para acotar los peligros de tratar los datos como potenciales instrucciones.

4.2. El cuello de botella de von Neumann

La memoria única conlleva una limitación estructural conocida como cuello de botella de von Neumann (von Neumann bottleneck), término acuñado por John Backus en su conferencia Turing de 1977. Como instrucciones y datos comparten un mismo bus para viajar entre memoria y procesador, no pueden transferirse simultáneamente: la CPU debe alternar entre buscar la instrucción y buscar los datos que esta manipula. A medida que los procesadores se aceleraron mucho más que las memorias, este cuello de botella se convirtió en el principal limitador del rendimiento. Es lo que a veces se denomina el muro de la memoria (memory wall): entre 1980 y 2010, la velocidad de la CPU creció a un ritmo cercano al 50 % anual, mientras que la latencia de la DRAM apenas mejoraba un 7 % anual, abriendo una brecha de dos órdenes de magnitud. Hoy un acceso a memoria principal que falla en todas las cachés puede costar del orden de 200 a 300 ciclos de reloj, tiempo durante el cual un procesador superescalar moderno podría haber completado cientos de operaciones. Buena parte de la evolución arquitectónica posterior —memorias caché, segmentación, predicción de saltos, ejecución fuera de orden, prebúsqueda— puede leerse como una batalla constante por mitigar esta restricción.

4.3. La arquitectura Harvard

La arquitectura Harvard, cuyo nombre proviene del computador electromecánico Harvard Mark I, separa físicamente la memoria de instrucciones de la memoria de datos, dotando a cada una de su propio bus. Esto permite buscar una instrucción y acceder a un dato en el mismo ciclo, duplicando el ancho de banda efectivo con la memoria y eliminando, en gran medida, el cuello de botella.

Podemos cuantificar la ventaja con un ejemplo sencillo. Supongamos un procesador con un bus de datos de 64 bits (8 bytes) que opera a una frecuencia efectiva de transferencia de 2 \times 10^{9} transferencias por segundo. En una organización von Neumann pura con un único bus, el ancho de banda máximo se reparte entre instrucciones y datos:

BW = 8\ \text{bytes} \times 2\times 10^{9}\ \text{s}^{-1} = 16\ \text{GB/s (compartidos)}.

En una organización Harvard con dos buses idénticos independientes, ese mismo ancho de banda se duplica a 32 GB/s efectivos, porque instrucción y dato viajan en paralelo por caminos distintos. La diferencia, en un bucle apretado que consume una palabra de datos por instrucción, se traduce directamente en el doble de rendimiento potencial.

La contrapartida es la pérdida de flexibilidad: al no compartir el espacio de direcciones, resulta más complejo que un programa se trate a sí mismo como dato, y se duplican las líneas de memoria. Por ello, la arquitectura Harvard pura predomina en microcontroladores y procesadores de señal digital (DSP) —donde el programa suele residir en memoria de solo lectura y la separación es natural—, mientras que la von Neumann domina en los ordenadores de propósito general. En un microcontrolador para automoción o electrodomésticos, por ejemplo, el firmware se graba en memoria flash y los datos viven en RAM: la separación es intrínseca al problema.

La síntesis dominante hoy es la arquitectura Harvard modificada: los procesadores de propósito general presentan al programador un espacio de direcciones único (von Neumann), pero internamente, en el nivel de la caché de primer nivel, separan caché de instrucciones (caché-I) y caché de datos (caché-D). Así obtienen el doble ancho de banda de Harvard en la zona crítica próxima al núcleo, conservando la flexibilidad del modelo de programa almacenado. Esta hibridación es la norma en las microarquitecturas actuales, tanto de la familia x86-64 como de las basadas en ARM y RISC-V.

Característica von Neumann Harvard pura
Memoria de instrucciones y datos Compartida Separada
Buses Uno compartido Dos independientes
Acceso simultáneo instr./dato No
Programa como dato modificable Sencillo Difícil
Coste y complejidad de líneas Menor Mayor (duplicadas)
Ámbito típico Propósito general Microcontroladores, DSP
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 420" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <rect x="0" y="0" width="760" height="420" fill="#ffffff"/> <line x1="380" y1="20" x2="380" y2="400" stroke="#e2e8f0" stroke-width="1.5"/> <text x="190" y="42" text-anchor="middle" font-size="17" font-weight="700" fill="#0f172a">Arquitectura von Neumann</text> <rect x="100" y="70" width="180" height="56" rx="6" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="190" y="104" text-anchor="middle" font-size="15" font-weight="700" fill="#0f172a">CPU</text> <polygon points="190,130 183,144 197,144" fill="#2563eb"/> <line x1="190" y1="144" x2="190" y2="246" stroke="#2563eb" stroke-width="2.5"/> <polygon points="190,260 183,246 197,246" fill="#2563eb"/> <text x="204" y="200" text-anchor="start" font-size="11" font-style="italic" fill="#64748b">bus único</text> <rect x="90" y="264" width="200" height="64" rx="6" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="190" y="290" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">Memoria única</text> <text x="190" y="310" text-anchor="middle" font-size="11" fill="#334155">instrucciones + datos</text> <text x="190" y="382" text-anchor="middle" font-size="10.5" font-style="italic" fill="#64748b">un solo camino → cuello de botella</text> <text x="570" y="42" text-anchor="middle" font-size="17" font-weight="700" fill="#0f172a">Arquitectura Harvard</text> <rect x="475" y="70" width="190" height="40" rx="6" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="570" y="95" text-anchor="middle" font-size="12" fill="#0f172a">Memoria de instrucciones</text> <polygon points="570,114 563,128 577,128" fill="#2563eb"/> <line x1="570" y1="128" x2="570" y2="164" stroke="#2563eb" stroke-width="2.5"/> <polygon points="570,178 563,164 577,164" fill="#2563eb"/> <text x="584" y="150" text-anchor="start" font-size="10.5" font-style="italic" fill="#64748b">bus de instrucciones</text> <rect x="480" y="182" width="180" height="56" rx="6" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="570" y="216" text-anchor="middle" font-size="15" font-weight="700" fill="#0f172a">CPU</text> <polygon points="570,242 563,256 577,256" fill="#059669"/> <line x1="570" y1="256" x2="570" y2="292" stroke="#059669" stroke-width="2.5"/> <polygon points="570,306 563,292 577,292" fill="#059669"/> <text x="584" y="278" text-anchor="start" font-size="10.5" font-style="italic" fill="#64748b">bus de datos</text> <rect x="475" y="310" width="190" height="40" rx="6" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="570" y="335" text-anchor="middle" font-size="12" fill="#0f172a">Memoria de datos</text> <text x="570" y="382" text-anchor="middle" font-size="10.5" font-style="italic" fill="#64748b">dos caminos → acceso simultáneo</text> </svg>

Figura 3. Modelo de von Neumann (memoria y bus únicos) frente a la arquitectura Harvard (memorias y buses separados).

5. La Unidad Central de Proceso (CPU): estructura y funcionamiento

La Unidad Central de Proceso o procesador es el elemento funcional que interpreta y ejecuta las instrucciones del programa; constituye el «cerebro» del ordenador. En la implementación física actual se integra en un único chip, el microprocesador, que puede contener varios núcleos (cores) capaces de ejecutar flujos de instrucciones de forma independiente. La CPU se organiza internamente en tres bloques funcionales: la unidad de control, la unidad aritmético-lógica y el banco de registros.

5.1. La Unidad de Control (UC)

La Unidad de Control dirige y coordina el funcionamiento de todo el ordenador. Su misión es interpretar cada instrucción y generar la secuencia temporizada de señales de control que activan los demás componentes: ordena a la ALU qué operación realizar, a los registros cuándo cargar o volcar su contenido, a la memoria cuándo leer o escribir, y a los buses cómo encaminar la información. Funciona sincronizada por la señal de reloj, un tren de pulsos que marca el ritmo de las operaciones; su frecuencia, medida en gigahercios, determina cuántos ciclos por segundo ejecuta el procesador. A modo de referencia numérica, un reloj de 4 GHz impone un periodo de ciclo de T = 1/(4\times10^{9}) = 0{,}25 nanosegundos; en ese brevísimo intervalo la señal eléctrica, viajando a una fracción de la velocidad de la luz, recorre apenas unos centímetros, lo que ilustra por qué la miniaturización extrema es condición necesaria para las altas frecuencias.

Existen dos grandes filosofías de implementación de la UC:

  • Control cableado (hardwired): la lógica de control se construye con circuitos combinacionales y secuenciales fijos, típicamente una máquina de estados finitos. Es rápido y eficiente, pero rígido y difícil de modificar. Predomina en arquitecturas RISC, con conjuntos de instrucciones reducidos y regulares.
  • Control microprogramado: cada instrucción de máquina se descompone en una secuencia de microinstrucciones almacenadas en una memoria de control interna (la microcódigo). Es flexible y facilita instrucciones complejas, además de permitir corregir errores del procesador mediante actualizaciones de microcódigo cargadas por el firmware. Su coste es cierta lentitud añadida. Es característico de arquitecturas CISC, como la x86 clásica, que internamente traduce sus instrucciones complejas a microoperaciones (micro-ops) de tipo RISC ejecutadas por un núcleo de ejecución muy segmentado.

5.2. RISC frente a CISC

La distinción entre CISC (Complex Instruction Set Computer) y RISC (Reduced Instruction Set Computer) es una de las grandes dicotomías de la arquitectura y suele ser objeto de pregunta. La filosofía CISC, encarnada por la familia x86, ofrece un repertorio amplio de instrucciones potentes, de longitud variable, capaces de operar directamente sobre memoria y de realizar en una sola instrucción tareas complejas; nació cuando la memoria era escasa y cara y el código compacto resultaba valioso. La filosofía RISC, formulada a comienzos de los años ochenta por Patterson y por Hennessy, parte de la observación de que los compiladores apenas usaban las instrucciones complejas y de que un repertorio pequeño, regular y de longitud fija se segmenta con mucha mayor facilidad. Sus rasgos característicos son las instrucciones de tamaño uniforme, el modelo carga/almacenamiento (solo instrucciones específicas acceden a memoria y el resto operan únicamente entre registros), un banco de registros amplio y modos de direccionamiento sencillos.

Rasgo CISC (p. ej. x86) RISC (p. ej. ARM, RISC-V)
Número de instrucciones Elevado Reducido
Longitud de instrucción Variable Fija (o casi)
Acceso a memoria Muchas instrucciones Solo carga/almacenamiento
Modos de direccionamiento Numerosos Pocos
Registros de propósito general Pocos (históricamente) Muchos
Implementación de la UC Microprogramada Cableada
Segmentación Difícil Natural

La frontera se ha difuminado: los procesadores x86 modernos son CISC de cara al programador pero RISC en su interior, pues descomponen cada instrucción compleja en micro-ops sencillas. Aun así, la arquitectura del repertorio (la ISA visible) sigue determinando aspectos de compatibilidad, licenciamiento y ecosistema que explican por qué ARM domina la computación móvil y por qué RISC-V, al ser abierta, gana terreno en sistemas empotrados y aceleradores.

5.3. La Unidad Aritmético-Lógica (ALU)

La Unidad Aritmético-Lógica es el circuito combinacional encargado de realizar las operaciones de cálculo: aritméticas (suma, resta, y en procesadores más complejos multiplicación y división) y lógicas (AND, OR, NOT, XOR), además de desplazamientos y comparaciones. Recibe uno o dos operandos, una señal que selecciona la operación, y produce un resultado junto con una serie de indicadores de estado o flags que se almacenan en el registro de estado: bit de cero (Z), de acarreo (C, carry), de signo (N), de desbordamiento (V, overflow) y de paridad, entre otros. Estos indicadores son esenciales para las instrucciones de salto condicional, pues permiten al programa tomar decisiones según el resultado de operaciones previas.

Conviene precisar la diferencia entre acarreo y desbordamiento, matiz que separa una respuesta correcta de una excelente. El acarreo señala que el resultado excede el rango de la aritmética sin signo, mientras que el desbordamiento señala que excede el rango de la aritmética en complemento a dos. Con 8 bits, sumar 0111 1111 (127) y 0000 0001 (1) produce 1000 0000: sin signo el resultado (128) es correcto y no hay acarreo, pero en complemento a dos representa −128, un resultado erróneo respecto a la suma esperada de 128, de modo que se activa el desbordamiento. La ALU calcula ambos indicadores simultáneamente y es el programa —a través del compilador— quien decide cuál interpretar según el tipo de dato. La condición de desbordamiento en complemento a dos puede expresarse compactamente como la desigualdad entre el acarreo de entrada y el de salida del bit de signo:

V = C_{n-1} \oplus C_{n},

donde \oplus denota la operación XOR y C_i es el acarreo hacia el bit i.

Las operaciones en coma flotante, según el estándar IEEE 754, suelen delegarse en una unidad especializada, la FPU (Floating Point Unit), y las operaciones sobre vectores en unidades SIMD (Single Instruction, Multiple Data), como las extensiones SSE y AVX en x86 o NEON y SVE en ARM. Conceptualmente, todas son ampliaciones de la función básica de la ALU. En los procesadores contemporáneos coexisten, además, unidades matriciales y de multiplicación-acumulación optimizadas para inteligencia artificial (las llamadas tensor o matrix units), que aceleran el producto de matrices en baja precisión.

5.4. Los registros

Los registros son celdas de memoria de altísima velocidad, internas a la CPU, que almacenan temporalmente datos, direcciones e instrucciones en curso. Su acceso es prácticamente instantáneo, sin la latencia de la memoria principal. Se distinguen:

  • Registros visibles al programador o de propósito general, que albergan operandos y resultados intermedios.
  • Registros especializados, entre los que destacan:
    • Contador de Programa (PC) o registro de instrucción siguiente: contiene la dirección de la próxima instrucción a ejecutar.
    • Registro de Instrucción (IR): almacena la instrucción que está siendo decodificada y ejecutada.
    • Registro de Dirección de Memoria (MAR): contiene la dirección a la que se va a acceder en memoria.
    • Registro de Datos de Memoria (MDR o MBR): actúa como búfer del dato que se lee o escribe en memoria.
    • Acumulador: en arquitecturas clásicas, registro donde la ALU deposita resultados.
    • Registro de Estado o de banderas: alberga los flags generados por la ALU.
    • Puntero de Pila (SP): apunta a la cima de la pila del sistema, fundamental para la gestión de subrutinas e interrupciones.

Conviene distinguir los registros arquitectónicos —los que la ISA hace visibles y que el programador de ensamblador manipula— de los registros físicos, mucho más numerosos, que el hardware emplea internamente. Mediante la técnica de renombramiento de registros (register renaming), un procesador fuera de orden asocia dinámicamente cada registro arquitectónico a distintos registros físicos, eliminando dependencias falsas (de nombre) y permitiendo que instrucciones que solo comparten el nombre de un registro, pero no una dependencia real de datos, se ejecuten en paralelo. Un núcleo x86 actual puede exponer 16 registros de propósito general y disponer, sin embargo, de más de 150 registros físicos internos.

El modelo de ejecución de una ISA se clasifica según dónde residen los operandos: máquinas de acumulador (un operando implícito), de pila (operandos en la cima de una pila, como la máquina virtual de Java), de registro-memoria (un operando puede estar en memoria, típico de x86) y de registro-registro o carga/almacenamiento (los operandos aritméticos deben estar en registros, típico de RISC). Esta clasificación condiciona el número de accesos a memoria por instrucción y, con ello, el rendimiento.

El conjunto de instrucciones que la CPU puede ejecutar, junto con sus formatos, modos de direccionamiento y registros visibles, constituye la Arquitectura del Conjunto de Instrucciones (ISA), la frontera precisa entre el hardware y el software: es el contrato que garantiza que un mismo binario funcione en implementaciones muy distintas de la misma familia. Debe distinguirse de la microarquitectura, que es la realización concreta de esa ISA (número de etapas del cauce, tamaño de las cachés, unidades de ejecución). Ejemplos vigentes de ISA son x86-64, ARM (AArch64) y la arquitectura abierta RISC-V, que ha ganado relevancia notable por su carácter libre y modular. Un matiz adicional de rigor es la ordenación de bytes o endianness: en el formato little-endian (x86, RISC-V) el byte menos significativo ocupa la dirección más baja, mientras que en big-endian (habitual en protocolos de red) ocurre lo contrario; ignorarlo es fuente clásica de errores al intercambiar datos binarios entre sistemas.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 780 510" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <rect x="140" y="40" width="480" height="440" rx="10" fill="#f8fafc" stroke="#334155" stroke-width="2"/> <text x="380" y="66" text-anchor="middle" font-size="17" font-weight="700" fill="#0f172a">CPU (microprocesador)</text> <rect x="160" y="95" width="170" height="80" rx="8" fill="#eff6ff" stroke="#2563eb" stroke-width="1.5"/> <text x="245" y="120" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">Unidad de Control (UC)</text> <text x="245" y="141" text-anchor="middle" font-size="11" fill="#64748b">secuencia y decodifica</text> <text x="245" y="157" text-anchor="middle" font-size="11" fill="#64748b">instrucciones</text> <rect x="430" y="95" width="175" height="80" rx="8" fill="#eff6ff" stroke="#2563eb" stroke-width="1.5"/> <text x="517" y="124" text-anchor="middle" font-size="14" font-weight="700" fill="#0f172a">ALU</text> <text x="517" y="144" text-anchor="middle" font-size="11" fill="#64748b">Unidad Aritmético-Lógica</text> <rect x="455" y="187" width="125" height="30" rx="6" fill="#fef3c7" stroke="#64748b" stroke-width="1.2"/> <text x="517" y="207" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">flags: Z N C V</text> <line x1="330" y1="135" x2="421" y2="135" stroke="#334155" stroke-width="1.5" stroke-dasharray="5,4"/> <polygon points="430,135 420,130 420,140" fill="#334155"/> <text x="380" y="105" text-anchor="middle" font-size="10.5" font-style="italic" fill="#059669">señales de</text> <text x="380" y="120" text-anchor="middle" font-size="10.5" font-style="italic" fill="#059669">control</text> <line x1="245" y1="175" x2="245" y2="290" stroke="#334155" stroke-width="1.5"/> <polygon points="245,300 240,290 250,290" fill="#334155"/> <text x="235" y="240" text-anchor="end" font-size="10.5" font-style="italic" fill="#059669">control</text> <line x1="445" y1="185" x2="445" y2="290" stroke="#334155" stroke-width="1.5"/> <polygon points="445,175 440,187 450,187" fill="#334155"/> <polygon points="445,300 440,288 450,288" fill="#334155"/> <text x="437" y="235" text-anchor="end" font-size="10.5" font-style="italic" fill="#2563eb">operandos /</text> <text x="437" y="250" text-anchor="end" font-size="10.5" font-style="italic" fill="#2563eb">resultado</text> <rect x="160" y="300" width="440" height="165" rx="8" fill="#ecfdf5" stroke="#059669" stroke-width="1.5"/> <text x="380" y="325" text-anchor="middle" font-size="14" font-weight="700" fill="#0f172a">Banco de registros</text> <rect x="175" y="340" width="95" height="32" rx="5" fill="#f8fafc" stroke="#64748b" stroke-width="1"/> <text x="222" y="361" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">PC</text> <rect x="280" y="340" width="95" height="32" rx="5" fill="#f8fafc" stroke="#64748b" stroke-width="1"/> <text x="327" y="361" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">IR</text> <rect x="385" y="340" width="95" height="32" rx="5" fill="#f8fafc" stroke="#64748b" stroke-width="1"/> <text x="432" y="361" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">MAR</text> <rect x="490" y="340" width="95" height="32" rx="5" fill="#f8fafc" stroke="#64748b" stroke-width="1"/> <text x="537" y="361" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">MDR</text> <rect x="175" y="382" width="95" height="32" rx="5" fill="#f8fafc" stroke="#64748b" stroke-width="1"/> <text x="222" y="403" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">ACC</text> <rect x="280" y="382" width="95" height="32" rx="5" fill="#f8fafc" stroke="#64748b" stroke-width="1"/> <text x="327" y="403" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">SP</text> <rect x="385" y="382" width="200" height="32" rx="5" fill="#f8fafc" stroke="#64748b" stroke-width="1"/> <text x="485" y="403" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">Estado</text> <rect x="175" y="424" width="410" height="32" rx="5" fill="#eff6ff" stroke="#64748b" stroke-width="1"/> <text x="380" y="445" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">R0 ... Rn (propósito general)</text> <polyline points="25,111 25,95 45,95 45,127 65,127 65,95 85,95 85,127 105,127 105,111" fill="none" stroke="#334155" stroke-width="1.8"/> <text x="65" y="147" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">reloj</text> <line x1="105" y1="111" x2="150" y2="111" stroke="#334155" stroke-width="1.5"/> <polygon points="160,111 150,106 150,116" fill="#334155"/> <line x1="620" y1="260" x2="743" y2="260" stroke="#334155" stroke-width="1.8"/> <polygon points="755,260 743,255 743,265" fill="#334155"/> <text x="692" y="228" text-anchor="middle" font-size="11" font-style="italic" fill="#64748b">hacia memoria y E/S</text> <text x="692" y="244" text-anchor="middle" font-size="11" font-style="italic" fill="#64748b">(buses)</text> </svg>

Figura 4. Estructura interna de la CPU: unidad de control, ALU y banco de registros (PC, IR, MAR, MDR, ACC, SP, estado…), gobernados por el reloj.

6. El ciclo de instrucción: búsqueda, decodificación y ejecución

El funcionamiento de la CPU se reduce, en su nivel más esencial, a la repetición incesante de un ciclo de instrucción (también llamado ciclo de máquina o ciclo fetch-decode-execute). Este ciclo es la realización física del comportamiento paso a paso de la máquina de Turing y constituye el latido del ordenador.

6.1. Fases del ciclo

El ciclo se descompone en tres fases fundamentales, que pueden detallarse así:

  1. Búsqueda (fetch): la Unidad de Control toma la dirección contenida en el Contador de Programa, la coloca en el Registro de Dirección de Memoria y emite una señal de lectura. La memoria devuelve la instrucción, que se aloja en el Registro de Datos de Memoria y, desde ahí, en el Registro de Instrucción. Acto seguido, el Contador de Programa se incrementa para apuntar a la siguiente instrucción.
  2. Decodificación (decode): la Unidad de Control interpreta el código de operación de la instrucción alojada en el Registro de Instrucción, determina qué acción debe realizarse y localiza los operandos según el modo de direccionamiento indicado (inmediato, directo, indirecto, por registro, indexado, relativo, etc.). Si los operandos están en memoria, se efectúan los accesos necesarios.
  3. Ejecución (execute): se lleva a cabo la operación. Si es aritmético-lógica, la ALU la realiza y deposita el resultado en el registro destino, actualizando los indicadores de estado. Si es una transferencia, se mueven datos entre registros o memoria. Si es un salto, se modifica el Contador de Programa para alterar el flujo secuencial.

Es ilustrativo describir la fase de búsqueda con notación de transferencia entre registros (RTL), que sintetiza con precisión las microoperaciones implicadas:

\text{MAR} \leftarrow \text{PC}; \quad \text{MDR} \leftarrow \text{Mem[MAR]}; \quad \text{IR} \leftarrow \text{MDR}; \quad \text{PC} \leftarrow \text{PC} + \ell,

donde \ell es la longitud de la instrucción en unidades direccionables (por ejemplo, 4 bytes en una ISA RISC de instrucciones fijas). Consideremos, como ejemplo completo, la ejecución de una instrucción de suma ADD R1, R2, R3 (que calcula R1 ← R2 + R3): tras la búsqueda anterior, la decodificación identifica el código de operación de suma y los tres registros; la ejecución encamina R2 y R3 a las entradas de la ALU, selecciona la operación de suma, y el resultado se escribe en R1 mientras se actualizan los flags. Ninguna de estas microoperaciones accede a memoria de datos, lo que explica por qué las instrucciones aritméticas RISC son tan veloces y regulares.

Algunos autores añaden una cuarta fase de escritura del resultado (write-back) y una fase de comprobación de interrupciones, en la que la CPU verifica si algún dispositivo solicita atención antes de iniciar el siguiente ciclo. Concluido el ciclo, comienza de nuevo con la instrucción que ahora señala el Contador de Programa, indefinidamente, hasta que una instrucción de parada o el sistema operativo detengan el proceso.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 560" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <rect x="0" y="0" width="720" height="560" fill="#ffffff"/> <text x="360" y="34" text-anchor="middle" font-size="17" font-weight="700" fill="#0f172a">Ciclo de instrucción (fetch · decode · execute)</text> <text x="360" y="54" text-anchor="middle" font-size="11" fill="#64748b">bucle cerrado: se repite por cada instrucción</text> <!-- Caja 1: BUSQUEDA (arriba) --> <rect x="190" y="80" width="340" height="100" rx="8" fill="#eff6ff" stroke="#2563eb" stroke-width="1.5"/> <rect x="206" y="96" width="26" height="26" rx="13" fill="#2563eb"/> <text x="219" y="114" text-anchor="middle" font-size="15" font-weight="700" fill="#ffffff">1</text> <text x="360" y="115" text-anchor="middle" font-size="14" font-weight="700" fill="#0f172a">BÚSQUEDA (fetch)</text> <text x="360" y="138" text-anchor="middle" font-size="12" fill="#334155">PC → MAR; leer memoria;</text> <text x="360" y="156" text-anchor="middle" font-size="12" fill="#334155">MBR → IR; PC ← PC + 1</text> <!-- Caja 2: DECODIFICACION (derecha) --> <rect x="450" y="230" width="250" height="100" rx="8" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <rect x="466" y="246" width="26" height="26" rx="13" fill="#334155"/> <text x="479" y="264" text-anchor="middle" font-size="15" font-weight="700" fill="#ffffff">2</text> <text x="575" y="265" text-anchor="middle" font-size="14" font-weight="700" fill="#0f172a">DECODIFICACIÓN</text> <text x="575" y="288" text-anchor="middle" font-size="12" fill="#334155">interpretar código de</text> <text x="575" y="306" text-anchor="middle" font-size="12" fill="#334155">operación; localizar operandos</text> <!-- Caja 3: EJECUCION (abajo) --> <rect x="190" y="380" width="340" height="100" rx="8" fill="#ecfdf5" stroke="#059669" stroke-width="1.5"/> <rect x="206" y="396" width="26" height="26" rx="13" fill="#059669"/> <text x="219" y="414" text-anchor="middle" font-size="15" font-weight="700" fill="#ffffff">3</text> <text x="360" y="415" text-anchor="middle" font-size="14" font-weight="700" fill="#0f172a">EJECUCIÓN (execute)</text> <text x="360" y="438" text-anchor="middle" font-size="12" fill="#334155">la ALU opera / transferencia / salto;</text> <text x="360" y="456" text-anchor="middle" font-size="12" fill="#334155">actualizar flags</text> <!-- Caja pequeña: comprobacion (izquierda) --> <rect x="20" y="230" width="200" height="100" rx="8" fill="#fef3c7" stroke="#d97706" stroke-width="1.5"/> <text x="120" y="272" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">¿interrupción</text> <text x="120" y="292" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">pendiente?</text> <!-- Flecha 1 -> 2 --> <path d="M 530 150 C 620 160 660 190 660 226" fill="none" stroke="#334155" stroke-width="2"/> <polygon points="660,234 654,222 666,222" fill="#334155"/> <!-- Flecha 2 -> 3 --> <path d="M 575 330 C 575 366 510 374 470 376" fill="none" stroke="#334155" stroke-width="2"/> <polygon points="462,376 474,370 474,382" fill="#334155"/> <!-- Flecha 3 -> comprobacion --> <path d="M 190 414 C 110 414 80 380 90 334" fill="none" stroke="#334155" stroke-width="2"/> <polygon points="92,326 98,338 86,338" fill="#334155"/> <!-- Flecha comprobacion -> 1 (retorno / repetir) --> <path d="M 120 230 C 120 150 130 120 186 112" fill="none" stroke="#2563eb" stroke-width="2.2"/> <polygon points="194,111 182,105 183,117" fill="#2563eb"/> <!-- Etiqueta repetir sobre la flecha de retorno --> <rect x="44" y="150" width="118" height="44" rx="6" fill="#eff6ff" stroke="#2563eb" stroke-width="1.2"/> <path d="M 62 165 a 9 9 0 1 1 -3 -6" fill="none" stroke="#2563eb" stroke-width="2"/> <polygon points="60,156 66,162 56,164" fill="#2563eb"/> <text x="112" y="170" text-anchor="middle" font-size="13" font-weight="700" fill="#2563eb">↻ repetir</text> <text x="103" y="187" text-anchor="middle" font-size="10" fill="#64748b">siguiente instrucción</text> </svg>

Figura 5. El ciclo de instrucción como bucle: búsqueda (fetch), decodificación (decode) y ejecución (execute), con comprobación de interrupciones.

6.2. La ecuación del rendimiento

Para razonar cuantitativamente sobre la velocidad de un procesador se emplea la llamada ecuación fundamental del rendimiento o «ley de hierro» (iron law), que expresa el tiempo de CPU de un programa como producto de tres factores:

T_{\text{CPU}} = N_{\text{instr}} \times \text{CPI} \times T_{\text{ciclo}},

donde N_{\text{instr}} es el número de instrucciones ejecutadas, CPI es el número medio de ciclos por instrucción y T_{\text{ciclo}} es el periodo de reloj. Esta descomposición es didácticamente valiosísima porque muestra que cada factor está gobernado por una capa distinta: N_{\text{instr}} depende del algoritmo, del compilador y de la ISA; el CPI depende de la microarquitectura (segmentación, cachés, ejecución superescalar); y T_{\text{ciclo}} depende de la tecnología de fabricación y de la frecuencia. Optimizar el rendimiento exige actuar sobre los tres, y mejorar uno a costa de empeorar otro puede ser contraproducente. Por ejemplo, una ISA CISC reduce N_{\text{instr}} pero tiende a aumentar el CPI; una RISC hace lo contrario. Como ejemplo numérico, un programa de 10^{9} instrucciones con un CPI de 1,5 en un procesador a 4 GHz tarda 10^{9} \times 1{,}5 \times 0{,}25\,\text{ns} = 0{,}375 segundos.

6.3. Interrupciones

El mecanismo de interrupción permite que un suceso externo o interno (la finalización de una operación de E/S, un error aritmético, una señal de reloj) interrumpa la ejecución normal. La CPU guarda su estado —el Contador de Programa y los registros pertinentes en la pila—, atiende la rutina de servicio de interrupción y, al terminar, restaura el estado y reanuda el programa interrumpido. Las interrupciones son el fundamento de la multitarea, de la respuesta a eventos y de la comunicación eficiente con los periféricos, pues evitan la espera activa (consultar repetidamente si un dispositivo está listo) en favor de una atención bajo demanda.

Conviene distinguir tres categorías, que un tribunal aprecia bien delimitadas. Las interrupciones hardware proceden de dispositivos externos y son asíncronas respecto al programa. Las excepciones o interrupciones internas las genera la propia CPU al detectar una condición durante la ejecución (una división por cero, un fallo de página, un acceso a memoria no permitido). Y las llamadas al sistema o interrupciones software (syscall, trap) las provoca deliberadamente el programa para solicitar un servicio al sistema operativo, y son el mecanismo que separa el modo usuario del modo privilegiado o supervisor, piedra angular de la protección. Cuando concurren varias, un controlador de interrupciones las prioriza y puede encadenar interrupciones anidadas.

6.4. Segmentación y paralelismo

Ejecutar las fases de manera estrictamente secuencial desaprovecha el hardware, pues mientras se decodifica una instrucción la unidad de búsqueda está ociosa. La segmentación (pipelining) solapa la ejecución de varias instrucciones, de modo que mientras una se ejecuta, la siguiente se decodifica y la tercera se busca, a la manera de una cadena de montaje. El cauce clásico RISC de cinco etapas —búsqueda de instrucción (IF), decodificación y lectura de registros (ID), ejecución en la ALU (EX), acceso a memoria (MEM) y escritura de resultado (WB)— idealmente finaliza una instrucción por ciclo una vez lleno el cauce, aunque cada instrucción individual sigue tardando cinco ciclos en atravesarlo. La ganancia teórica de un cauce de k etapas se aproxima al factor k para un número grande de instrucciones, pues el tiempo de ejecutar n instrucciones pasa de n\cdot k ciclos (secuencial) a k + (n-1) ciclos (segmentado).

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 740 420" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <text x="370" y="26" text-anchor="middle" font-size="17" font-weight="700" fill="#0f172a">Cronograma del cauce segmentado (5 etapas)</text> <text x="406" y="52" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">Ciclo de reloj &#8594;</text> <rect x="40" y="65" width="60" height="30" rx="3" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="70" y="85" text-anchor="middle" font-size="11" font-weight="700" fill="#334155">Instr.</text> <rect x="100" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="134" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">1</text> <rect x="168" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="202" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">2</text> <rect x="236" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="270" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">3</text> <rect x="304" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="338" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">4</text> <rect x="372" y="65" width="68" height="30" fill="#fef3c7" stroke="#e2e8f0" stroke-width="1"/> <text x="406" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">5</text> <rect x="440" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="474" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">6</text> <rect x="508" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="542" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">7</text> <rect x="576" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="610" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">8</text> <rect x="644" y="65" width="68" height="30" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="678" y="85" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">9</text> <line x1="100" y1="95" x2="100" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="168" y1="95" x2="168" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="236" y1="95" x2="236" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="304" y1="95" x2="304" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="372" y1="95" x2="372" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="440" y1="95" x2="440" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="508" y1="95" x2="508" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="576" y1="95" x2="576" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="644" y1="95" x2="644" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="712" y1="95" x2="712" y2="295" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="95" x2="712" y2="95" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="135" x2="712" y2="135" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="175" x2="712" y2="175" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="215" x2="712" y2="215" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="255" x2="712" y2="255" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="295" x2="712" y2="295" stroke="#e2e8f0" stroke-width="1"/> <rect x="40" y="95" width="60" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="70" y="120" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">I1</text> <rect x="40" y="135" width="60" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="70" y="160" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">I2</text> <rect x="40" y="175" width="60" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="70" y="200" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">I3</text> <rect x="40" y="215" width="60" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="70" y="240" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">I4</text> <rect x="40" y="255" width="60" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="70" y="280" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">I5</text> <rect x="100" y="95" width="68" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="134" y="119" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">IF</text> <rect x="168" y="95" width="68" height="40" fill="#ecfdf5" stroke="#334155" stroke-width="1.2"/> <text x="202" y="119" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">ID</text> <rect x="236" y="95" width="68" height="40" fill="#fef3c7" stroke="#334155" stroke-width="1.2"/> <text x="270" y="119" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">EX</text> <rect x="304" y="95" width="68" height="40" fill="#ea580c" opacity="0.15"/> <rect x="304" y="95" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="338" y="119" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">MEM</text> <rect x="372" y="95" width="68" height="40" fill="#64748b" opacity="0.18"/> <rect x="372" y="95" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="406" y="119" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">WB</text> <rect x="168" y="135" width="68" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="202" y="159" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">IF</text> <rect x="236" y="135" width="68" height="40" fill="#ecfdf5" stroke="#334155" stroke-width="1.2"/> <text x="270" y="159" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">ID</text> <rect x="304" y="135" width="68" height="40" fill="#fef3c7" stroke="#334155" stroke-width="1.2"/> <text x="338" y="159" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">EX</text> <rect x="372" y="135" width="68" height="40" fill="#ea580c" opacity="0.15"/> <rect x="372" y="135" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="406" y="159" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">MEM</text> <rect x="440" y="135" width="68" height="40" fill="#64748b" opacity="0.18"/> <rect x="440" y="135" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="474" y="159" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">WB</text> <rect x="236" y="175" width="68" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="270" y="199" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">IF</text> <rect x="304" y="175" width="68" height="40" fill="#ecfdf5" stroke="#334155" stroke-width="1.2"/> <text x="338" y="199" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">ID</text> <rect x="372" y="175" width="68" height="40" fill="#fef3c7" stroke="#334155" stroke-width="1.2"/> <text x="406" y="199" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">EX</text> <rect x="440" y="175" width="68" height="40" fill="#ea580c" opacity="0.15"/> <rect x="440" y="175" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="474" y="199" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">MEM</text> <rect x="508" y="175" width="68" height="40" fill="#64748b" opacity="0.18"/> <rect x="508" y="175" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="542" y="199" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">WB</text> <rect x="304" y="215" width="68" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="338" y="239" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">IF</text> <rect x="372" y="215" width="68" height="40" fill="#ecfdf5" stroke="#334155" stroke-width="1.2"/> <text x="406" y="239" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">ID</text> <rect x="440" y="215" width="68" height="40" fill="#fef3c7" stroke="#334155" stroke-width="1.2"/> <text x="474" y="239" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">EX</text> <rect x="508" y="215" width="68" height="40" fill="#ea580c" opacity="0.15"/> <rect x="508" y="215" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="542" y="239" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">MEM</text> <rect x="576" y="215" width="68" height="40" fill="#64748b" opacity="0.18"/> <rect x="576" y="215" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="610" y="239" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">WB</text> <rect x="372" y="255" width="68" height="40" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <text x="406" y="279" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">IF</text> <rect x="440" y="255" width="68" height="40" fill="#ecfdf5" stroke="#334155" stroke-width="1.2"/> <text x="474" y="279" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">ID</text> <rect x="508" y="255" width="68" height="40" fill="#fef3c7" stroke="#334155" stroke-width="1.2"/> <text x="542" y="279" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">EX</text> <rect x="576" y="255" width="68" height="40" fill="#ea580c" opacity="0.15"/> <rect x="576" y="255" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="610" y="279" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">MEM</text> <rect x="644" y="255" width="68" height="40" fill="#64748b" opacity="0.18"/> <rect x="644" y="255" width="68" height="40" fill="none" stroke="#334155" stroke-width="1.2"/> <text x="678" y="279" text-anchor="middle" font-size="12" font-weight="700" fill="#0f172a">WB</text> <rect x="372" y="95" width="68" height="200" fill="none" stroke="#dc2626" stroke-width="2.5"/> <text x="406" y="313" text-anchor="middle" font-size="10.5" font-style="italic" font-weight="700" fill="#dc2626">cauce lleno: 5 etapas activas a la vez</text> <rect x="40" y="336" width="14" height="14" fill="#eff6ff" stroke="#334155" stroke-width="1"/> <text x="60" y="347" font-size="10.5" fill="#334155">IF: búsqueda</text> <rect x="170" y="336" width="14" height="14" fill="#ecfdf5" stroke="#334155" stroke-width="1"/> <text x="190" y="347" font-size="10.5" fill="#334155">ID: decodif.</text> <rect x="300" y="336" width="14" height="14" fill="#fef3c7" stroke="#334155" stroke-width="1"/> <text x="320" y="347" font-size="10.5" fill="#334155">EX: ejecución</text> <rect x="440" y="336" width="14" height="14" fill="#ea580c" opacity="0.15"/> <rect x="440" y="336" width="14" height="14" fill="none" stroke="#334155" stroke-width="1"/> <text x="460" y="347" font-size="10.5" fill="#334155">MEM: memoria</text> <rect x="580" y="336" width="14" height="14" fill="#64748b" opacity="0.18"/> <rect x="580" y="336" width="14" height="14" fill="none" stroke="#334155" stroke-width="1"/> <text x="600" y="347" font-size="10.5" fill="#334155">WB: escritura</text> <rect x="40" y="372" width="500" height="32" rx="5" fill="#ecfdf5" stroke="#059669" stroke-width="1.2"/> <text x="54" y="392" font-size="11" fill="#0f172a">Régimen permanente: se completa 1 instrucción por ciclo (IPC ideal = 1).</text> </svg>

Figura 6. Cronograma del cauce segmentado (pipeline) clasico de cinco etapas.

En la práctica, los riesgos (hazards) impiden alcanzar ese ideal. Los riesgos estructurales surgen cuando dos etapas compiten por el mismo recurso; los riesgos de datos aparecen cuando una instrucción necesita un resultado que otra aún no ha escrito, y se mitigan con el adelantamiento de operandos (forwarding) o, si no basta, con burbujas de detención; y los riesgos de control, causados por los saltos, obligan a esperar a conocer el destino o a recurrir a la predicción de saltos y a la ejecución especulativa, descartando el trabajo si la predicción falla. Los predictores modernos superan el 95 % de aciertos, cifra imprescindible dado que aproximadamente una de cada cinco instrucciones es un salto.

Los procesadores actuales van más allá con el paralelismo a nivel de instrucción: las arquitecturas superescalares disponen de varias unidades de ejecución y lanzan varias instrucciones por ciclo (logrando un CPI inferior a 1, o su inverso, un IPC superior a 1); la ejecución fuera de orden (out-of-order) reordena las instrucciones para no detenerse ante dependencias; y el multihilo simultáneo (SMT, comercializado por Intel como Hyper-Threading) aprovecha los huecos de un núcleo para ejecutar varios hilos. Por encima, los procesadores multinúcleo ofrecen paralelismo a nivel de hilo y de proceso. La ganancia de este paralelismo está acotada por la ley de Amdahl, que establece que si una fracción f del trabajo es paralelizable y el resto es estrictamente secuencial, la aceleración máxima con p procesadores es

S(p) = \frac{1}{(1-f) + \dfrac{f}{p}},

de modo que, aunque p\to\infty, la aceleración nunca supera 1/(1-f): un programa con un 10 % de código secuencial no puede acelerarse más de diez veces por muchos núcleos que se le añadan. Este resultado explica por qué el paralelismo masivo no es una panacea y por qué la especialización del hardware ha cobrado tanta importancia. Conviene subrayar que todas estas técnicas son refinamientos sobre el mismo ciclo de instrucción básico; preservan la semántica del modelo de von Neumann (el programador percibe una ejecución secuencial y correcta) aunque internamente el orden físico de ejecución sea muy distinto.

7. El subsistema de memoria y su jerarquía

La memoria es el subsistema que almacena instrucciones y datos. Idealmente desearíamos una memoria a la vez muy rápida, de gran capacidad y de bajo coste, pero estas tres propiedades resultan tecnológicamente incompatibles: la memoria rápida es cara y pequeña, y la memoria barata y grande es lenta. La solución a este dilema es la jerarquía de memoria, una organización en niveles que combina distintas tecnologías para aproximarse al ideal a un coste razonable.

7.1. El principio de localidad

La jerarquía funciona porque los programas reales exhiben el principio de localidad de referencia, que tiene dos vertientes: la localidad temporal (si se accede a un dato, es probable que se vuelva a acceder pronto, como ocurre con la variable de control de un bucle) y la localidad espacial (si se accede a una posición, es probable que se acceda a posiciones cercanas, como ocurre al recorrer un vector o ejecutar instrucciones consecutivas). Gracias a la localidad, mantener cerca de la CPU una copia pequeña de los datos más usados resulta sorprendentemente eficaz. La localidad no es una ley física, sino una propiedad empírica del software real; los programas que la violan sistemáticamente —recorridos aleatorios de estructuras enormes, por ejemplo— rinden muy por debajo de su potencial precisamente porque derrotan a la jerarquía.

7.2. Los niveles de la jerarquía

De más rápido y pequeño a más lento y grande, los niveles típicos son:

  • Registros de la CPU: el nivel superior, de capacidad mínima (decenas de registros) y acceso en un ciclo.
  • Memoria caché, normalmente en tres niveles. La caché L1 (subdividida en instrucciones y datos, vestigio de la arquitectura Harvard) es la más rápida y pequeña, del orden de decenas de kibibytes e integrada por núcleo, con latencias de 3 a 5 ciclos; la L2 es mayor (cientos de kibibytes o algunos mebibytes) y algo más lenta; y la L3 suele ser de varios mebibytes y compartida por todos los núcleos. La caché se fabrica con memoria estática (SRAM), basada en biestables de seis transistores, rápida pero costosa y voluminosa.
  • Memoria principal o RAM, de tecnología dinámica (DRAM), que almacena la información en condensadores de un solo transistor y requiere refresco periódico porque los condensadores se descargan. Los módulos actuales siguen estándares de la familia DDR (los más recientes, DDR5), definidos por el comité JEDEC. Es volátil: pierde su contenido al cortar la alimentación.
  • Almacenamiento secundario: discos de estado sólido (SSD, basados en memoria flash NAND, conectados por interfaces como SATA o, sobre todo, NVMe sobre PCI Express) y discos duros magnéticos (HDD). Es no volátil, de gran capacidad y mucho más lento que la RAM.
  • Almacenamiento terciario o de respaldo: cintas y almacenamiento en la nube, de máxima capacidad y mínimo coste por bit, reservado a copias y archivo.

Para dimensionar los saltos de latencia entre niveles, resulta muy pedagógica la siguiente tabla de órdenes de magnitud aproximados en un sistema de sobremesa de 2026:

Nivel Latencia típica Capacidad típica Tecnología
Registro < 1 ns (1 ciclo) < 1 KiB Biestables
Caché L1 ≈ 1 ns (3-5 ciclos) 32-64 KiB por núcleo SRAM
Caché L2 ≈ 3-10 ns 0,5-2 MiB por núcleo SRAM
Caché L3 ≈ 10-30 ns 8-64 MiB compartida SRAM
RAM (DDR5) ≈ 80-100 ns 8-128 GiB DRAM
SSD NVMe ≈ 20-100 µs 0,5-8 TB Flash NAND
HDD ≈ 5-10 ms 1-20 TB Magnética

La lección que transmite esta tabla es contundente: entre un registro y un disco duro median unos siete órdenes de magnitud de latencia. Si un acceso a registro fuera un segundo, un acceso a disco duro equivaldría a meses. Toda la ingeniería de la jerarquía se dirige a que la inmensa mayoría de los accesos se resuelvan en los niveles altos.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 470" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <text x="360" y="34" text-anchor="middle" font-size="17" font-weight="700" fill="#0f172a">Jerarquía de memoria</text> <!-- Barra 1: Registros (más estrecha, arriba) --> <rect x="260" y="60" width="200" height="50" rx="4" fill="#fef3c7" stroke="#334155" stroke-width="1.5"/> <text x="360" y="90" text-anchor="middle" font-size="13" font-weight="600" fill="#0f172a">Registros de la CPU</text> <!-- Barra 2: Caché --> <rect x="220" y="120" width="280" height="50" rx="4" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/> <text x="360" y="150" text-anchor="middle" font-size="13" font-weight="600" fill="#0f172a">Caché L1 / L2 / L3 (SRAM)</text> <!-- Barra 3: RAM --> <rect x="175" y="180" width="370" height="50" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="360" y="210" text-anchor="middle" font-size="13" font-weight="600" fill="#0f172a">Memoria principal RAM (DRAM, DDR5)</text> <!-- Barra 4: Almacenamiento secundario --> <rect x="125" y="240" width="470" height="50" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="360" y="270" text-anchor="middle" font-size="13" font-weight="600" fill="#0f172a">Almacenamiento secundario (SSD NVMe, HDD)</text> <!-- Barra 5: Respaldo (más ancha, abajo) --> <rect x="80" y="300" width="560" height="50" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="2"/> <text x="360" y="330" text-anchor="middle" font-size="13" font-weight="600" fill="#0f172a">Respaldo (cinta, nube)</text> <!-- Flecha IZQUIERDA apuntando HACIA ARRIBA --> <line x1="45" y1="345" x2="45" y2="90" stroke="#2563eb" stroke-width="2.5"/> <polygon points="45,62 35,92 55,92" fill="#2563eb"/> <text x="22" y="300" text-anchor="middle" font-size="12" font-weight="600" fill="#2563eb" transform="rotate(-90 22 300)">+ velocidad + coste/bit − capacidad</text> <!-- Flecha DERECHA apuntando HACIA ABAJO --> <line x1="675" y1="90" x2="675" y2="320" stroke="#059669" stroke-width="2.5"/> <polygon points="675,348 665,318 685,318" fill="#059669"/> <text x="698" y="160" text-anchor="middle" font-size="12" font-weight="600" fill="#059669" transform="rotate(90 698 160)">+ capacidad − coste/bit − velocidad</text> <!-- Linea base separadora --> <line x1="40" y1="372" x2="680" y2="372" stroke="#e2e8f0" stroke-width="1"/> <text x="360" y="392" text-anchor="middle" font-size="11" fill="#64748b">Cima: rápida, cara y pequeña · Base: lenta, barata y de gran capacidad</text> </svg>

Figura 7. Jerarquía de memoria: a mayor cercanía a la CPU, más velocidad y coste por bit y menos capacidad.

Cuando la CPU solicita un dato, se busca primero en el nivel más cercano. Si está presente, se produce un acierto (hit); si no, un fallo (miss) que obliga a traerlo del nivel inferior, moviendo además el bloque o línea de caché que lo contiene (típicamente 64 bytes) para explotar la localidad espacial. La eficacia de la jerarquía se mide por la tasa de aciertos y por el tiempo medio de acceso.

7.3. Organización y políticas de la caché

Para ubicar una línea traída de memoria dentro de la caché existen tres organizaciones. En la correspondencia directa, cada bloque de memoria solo puede alojarse en una línea concreta de la caché, determinada por sus bits de índice; es simple y rápida pero sufre conflictos. En la totalmente asociativa, un bloque puede ir a cualquier línea, lo que minimiza los conflictos a costa de una búsqueda cara. El compromiso habitual es la asociativa por conjuntos (set-associative), donde la caché se divide en conjuntos y cada bloque puede ir a cualquier vía dentro de su conjunto (4, 8 o 16 vías son típicas).

El mecanismo se comprende mejor desglosando la dirección de memoria en tres campos: el desplazamiento (offset) dentro de la línea, el índice que selecciona el conjunto y la etiqueta (tag) que identifica el bloque. Con un ejemplo numérico: para una caché de correspondencia directa de 32 KiB con líneas de 64 bytes y direcciones de 32 bits, hay 32\,768/64 = 512 líneas; el desplazamiento ocupa \log_2 64 = 6 bits, el índice \log_2 512 = 9 bits y la etiqueta los 32 - 9 - 6 = 17 bits restantes. Al llegar una dirección, el hardware usa el índice para localizar la línea, compara su etiqueta almacenada con la de la dirección y, si coinciden y el bit de validez está activo, hay acierto y el desplazamiento selecciona el byte pedido.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 740 470" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <text x="370" y="24" text-anchor="middle" font-size="16" font-weight="700" fill="#0f172a">Dirección de 32 bits en una caché de correspondencia directa</text> <text x="40" y="58" text-anchor="start" font-size="9" fill="#64748b">bit 31</text> <text x="700" y="58" text-anchor="end" font-size="9" fill="#64748b">bit 0</text> <text x="400" y="42" text-anchor="middle" font-size="10" fill="#2563eb">etiqueta de la dirección</text> <polyline points="170,64 170,48 645,48 645,216" fill="none" stroke="#2563eb" stroke-width="1.3"/> <polygon points="645,224 641,216 649,216" fill="#2563eb"/> <rect x="40" y="64" width="260" height="40" rx="3" fill="#eff6ff" stroke="#334155" stroke-width="1.2"/> <rect x="300" y="64" width="180" height="40" rx="3" fill="#ecfdf5" stroke="#334155" stroke-width="1.2"/> <rect x="480" y="64" width="220" height="40" rx="3" fill="#fef3c7" stroke="#334155" stroke-width="1.2"/> <text x="170" y="82" text-anchor="middle" font-size="11.5" font-weight="700" fill="#0f172a">Etiqueta (tag)</text> <text x="170" y="97" text-anchor="middle" font-size="10" fill="#334155">17 bits</text> <text x="390" y="82" text-anchor="middle" font-size="11.5" font-weight="700" fill="#0f172a">Índice</text> <text x="390" y="97" text-anchor="middle" font-size="10" fill="#334155">9 bits</text> <text x="590" y="82" text-anchor="middle" font-size="11.5" font-weight="700" fill="#0f172a">Desplazamiento</text> <text x="590" y="97" text-anchor="middle" font-size="10" fill="#334155">(offset) · 6 bits</text> <text x="210" y="152" text-anchor="middle" font-size="10" fill="#059669">índice → línea 0</text> <polyline points="390,104 390,160 70,160 70,246 92,246" fill="none" stroke="#059669" stroke-width="1.4"/> <polygon points="100,246 92,242 92,250" fill="#059669"/> <text x="497" y="134" text-anchor="middle" font-size="10" fill="#ea580c">offset → byte</text> <polyline points="590,104 590,140 400,140 400,222" fill="none" stroke="#ea580c" stroke-width="1.4"/> <polygon points="400,230 396,222 404,222" fill="#ea580c"/> <rect x="100" y="198" width="460" height="180" fill="#ffffff" stroke="#334155" stroke-width="1.2"/> <rect x="100" y="230" width="460" height="32" fill="#eff6ff"/> <rect x="100" y="230" width="4" height="32" fill="#2563eb"/> <line x1="100" y1="230" x2="560" y2="230" stroke="#334155" stroke-width="1.2"/> <line x1="100" y1="262" x2="560" y2="262" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="292" x2="560" y2="292" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="322" x2="560" y2="322" stroke="#e2e8f0" stroke-width="1"/> <line x1="100" y1="348" x2="560" y2="348" stroke="#e2e8f0" stroke-width="1"/> <line x1="150" y1="198" x2="150" y2="378" stroke="#e2e8f0" stroke-width="1"/> <line x1="188" y1="198" x2="188" y2="378" stroke="#e2e8f0" stroke-width="1"/> <line x1="410" y1="198" x2="410" y2="378" stroke="#e2e8f0" stroke-width="1"/> <text x="125" y="218" text-anchor="middle" font-size="10.5" font-weight="700" fill="#334155">Línea</text> <text x="169" y="218" text-anchor="middle" font-size="10.5" font-weight="700" fill="#334155">V</text> <text x="299" y="218" text-anchor="middle" font-size="10.5" font-weight="700" fill="#334155">Datos (línea de 64 bytes)</text> <text x="485" y="218" text-anchor="middle" font-size="10.5" font-weight="700" fill="#334155">Etiqueta almacenada</text> <text x="125" y="250" text-anchor="middle" font-size="10" font-weight="700" fill="#0f172a">0</text> <text x="169" y="250" text-anchor="middle" font-size="10" font-weight="700" fill="#0f172a">1</text> <text x="299" y="250" text-anchor="middle" font-size="9" fill="#334155">byte 0 · byte 1 · … · byte 63</text> <text x="485" y="250" text-anchor="middle" font-size="10" font-weight="700" fill="#0f172a">0x1F0A2</text> <text x="125" y="281" text-anchor="middle" font-size="10" fill="#334155">1</text> <text x="169" y="281" text-anchor="middle" font-size="10" fill="#334155">0</text> <text x="299" y="281" text-anchor="middle" font-size="10" fill="#64748b">…</text> <text x="485" y="281" text-anchor="middle" font-size="10" fill="#334155">0x0C4B1</text> <text x="125" y="311" text-anchor="middle" font-size="10" fill="#334155">2</text> <text x="169" y="311" text-anchor="middle" font-size="10" fill="#334155">1</text> <text x="299" y="311" text-anchor="middle" font-size="10" fill="#64748b">…</text> <text x="485" y="311" text-anchor="middle" font-size="10" fill="#334155">0x0A77E</text> <text x="125" y="340" text-anchor="middle" font-size="12" fill="#64748b">⋮</text> <text x="169" y="340" text-anchor="middle" font-size="12" fill="#64748b">⋮</text> <text x="299" y="340" text-anchor="middle" font-size="12" fill="#64748b">⋮</text> <text x="485" y="340" text-anchor="middle" font-size="12" fill="#64748b">⋮</text> <text x="125" y="366" text-anchor="middle" font-size="10" fill="#334155">511</text> <text x="169" y="366" text-anchor="middle" font-size="10" fill="#334155">1</text> <text x="299" y="366" text-anchor="middle" font-size="10" fill="#64748b">…</text> <text x="485" y="366" text-anchor="middle" font-size="10" fill="#334155">0x1D902</text> <polyline points="560,246 592,246" fill="none" stroke="#2563eb" stroke-width="1.3"/> <polygon points="600,246 592,242 592,250" fill="#2563eb"/> <polygon points="645,224 690,246 645,268 600,246" fill="#f8fafc" stroke="#2563eb" stroke-width="1.5"/> <text x="645" y="251" text-anchor="middle" font-size="13" font-weight="700" fill="#2563eb">=?</text> <polyline points="645,268 645,274" fill="none" stroke="#0f172a" stroke-width="1.3"/> <polygon points="645,282 641,274 649,274" fill="#0f172a"/> <text x="645" y="298" text-anchor="middle" font-size="11" font-weight="700" fill="#0f172a">ACIERTO / FALLO</text> <text x="645" y="314" text-anchor="middle" font-size="9" font-style="italic" fill="#64748b">coinciden ⇒ acierto</text> <text x="330" y="396" text-anchor="middle" font-size="10.5" font-style="italic" fill="#64748b">512 líneas × 64 bytes = 32 KiB</text> <rect x="40" y="412" width="520" height="44" rx="6" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="52" y="430" text-anchor="start" font-size="10.5" font-weight="700" fill="#334155">Cálculo de los campos</text> <text x="52" y="449" text-anchor="start" font-size="10" fill="#334155">offset = log₂(64) = 6 | índice = log₂(512) = 9 | etiqueta = 32 − 9 − 6 = 17 bits</text> </svg>

Figura 8. Desglose de una direccion en etiqueta, indice y desplazamiento en una cache de correspondencia directa.

Cuando hay que hacer sitio a una línea nueva, una política de reemplazo decide cuál desalojar: las más habituales son LRU (Least Recently Used, la menos usada recientemente), FIFO y aleatoria, siendo LRU la que mejor explota la localidad temporal a cambio de más coste. Respecto a las escrituras, la escritura directa (write-through) actualiza a la vez caché y memoria, garantizando coherencia a costa de tráfico, mientras que la posescritura (write-back) actualiza solo la caché y difiere la escritura a memoria hasta el desalojo, marcando la línea como «sucia» con un bit dirty; esta última reduce drásticamente el tráfico y es la dominante. En sistemas multinúcleo, mantener consistentes las copias que cada núcleo guarda de un mismo dato exige un protocolo de coherencia de caché, como MESI (por los estados Modified, Exclusive, Shared, Invalid), que espía el bus para invalidar o actualizar copias obsoletas.

7.4. Tiempo medio de acceso a memoria

La métrica que integra todo lo anterior es el tiempo medio de acceso a memoria (AMAT, Average Memory Access Time), que para un nivel se expresa como

\text{AMAT} = t_{\text{acierto}} + \text{tasa de fallos} \times \text{penalización por fallo}.

Ilustrémoslo con un cálculo resoluble a mano. Supongamos una caché L1 con tiempo de acierto de 1 ciclo y una tasa de fallos del 5 %, respaldada por una memoria principal con una penalización de 100 ciclos. El tiempo medio será \text{AMAT} = 1 + 0{,}05 \times 100 = 6 ciclos. Si intercalamos una caché L2 con tiempo de acierto de 10 ciclos y una tasa de fallos local del 20 % (de modo que solo el 1 % de los accesos originales llega hasta la memoria principal), la penalización de fallo de L1 se convierte en el AMAT de L2, 10 + 0{,}20 \times 100 = 30 ciclos, y el tiempo total queda en \text{AMAT} = 1 + 0{,}05 \times 30 = 2{,}5 ciclos. La sola incorporación de un nivel intermedio ha reducido el tiempo medio de 6 a 2,5 ciclos: este cálculo, sencillo y de gran fuerza expositiva, justifica cuantitativamente la existencia de la jerarquía y suele impresionar a un tribunal.

7.5. Memoria virtual y tipos de memoria

El sistema operativo, con apoyo de la Unidad de Gestión de Memoria (MMU) del procesador, implementa la memoria virtual: cada proceso percibe un espacio de direcciones lineal y privado, potencialmente mayor que la memoria física, que se traduce a direcciones reales mediante tablas de páginas. La memoria se gestiona en bloques de tamaño fijo llamados páginas (habitualmente de 4 KiB, con soporte para páginas enormes de 2 MiB o 1 GiB que reducen la sobrecarga de traducción). La memoria virtual aporta tres beneficios capitales: el aislamiento entre procesos (uno no puede leer ni corromper la memoria de otro), la protección mediante permisos por página (lectura, escritura, ejecución) y la capacidad de ejecutar programas mayores que la RAM disponible, paginando hacia el disco las páginas menos usadas.

Como cada acceso lógico exigiría, en principio, un acceso adicional a la tabla de páginas para traducir la dirección, se emplea una caché de traducciones muy rápida, la TLB (Translation Lookaside Buffer), que almacena las traducciones recientes. Un fallo de TLB obliga a recorrer la tabla de páginas (el page-table walk), y un fallo de página propiamente dicho —cuando la página ni siquiera está en RAM— dispara una excepción que el sistema operativo atiende trayéndola del disco, operación millones de veces más lenta. Cuando la paginación se vuelve incesante porque el conjunto de trabajo no cabe en memoria, se produce el fenómeno de la hiperpaginación (thrashing), que degrada catastróficamente el rendimiento.

Atendiendo a su comportamiento, distinguimos la memoria de acceso aleatorio (RAM), de lectura y escritura y volátil, de la memoria de solo lectura (ROM) y sus variantes (PROM, EPROM, EEPROM y, en la práctica actual, memoria flash), no volátiles, donde reside el firmware de arranque —el BIOS/UEFI en los ordenadores personales—. La distinción ROM/RAM es esencial para comprender el proceso de arranque: el procesador, al encenderse, busca instrucciones en una dirección fija de memoria no volátil que inicia la carga del sistema operativo. Como referencia de la evolución tecnológica, la tabla siguiente resume los estándares recientes de DRAM síncrona:

Estándar Tasa de transferencia Ancho de banda por módulo (aprox.)
DDR3 800-2.133 MT/s 6,4-17 GB/s
DDR4 1.600-3.200 MT/s 12,8-25,6 GB/s
DDR5 4.800-8.400 MT/s 38,4-67,2 GB/s

8. El subsistema de entrada/salida (E/S)

El subsistema de entrada/salida es el que comunica el ordenador con el mundo exterior y con los dispositivos de almacenamiento. Sin él, la máquina sería un sistema cerrado e inútil. Comprende los periféricos (teclado, ratón, pantalla, impresora, sensores, redes, discos) y, sobre todo, los controladores o interfaces que median entre los periféricos y el resto del sistema.

8.1. El problema de la heterogeneidad

Los periféricos son extraordinariamente diversos en velocidad, naturaleza y formato de los datos: un teclado entrega unos pocos bytes por segundo de forma esporádica, mientras un SSD NVMe transfiere gigabytes por segundo, una diferencia de más de nueve órdenes de magnitud. Esta heterogeneidad se resuelve mediante controladores de E/S, módulos que adaptan las señales y los protocolos del dispositivo a los del bus del sistema, ofrecen registros de datos, de control y de estado, y desacoplan la velocidad del periférico de la de la CPU. El sistema operativo, a su vez, abstrae los controladores mediante controladores de dispositivo (drivers), de modo que las aplicaciones acceden a los periféricos a través de una interfaz uniforme y no necesitan conocer los detalles eléctricos de cada modelo. Esta doble capa de abstracción —hardware del controlador y software del driver— es la que permite que un mismo programa funcione con miles de dispositivos distintos.

8.2. Técnicas de gestión de la E/S

Existen tres técnicas fundamentales para gobernar las transferencias de E/S, de menor a mayor sofisticación:

  • E/S programada con sondeo (polling): la CPU consulta repetidamente el registro de estado del controlador hasta que el dispositivo está listo. Es simple pero ineficiente, pues malgasta ciclos en espera activa; solo se justifica en sistemas muy sencillos o cuando la espera prevista es brevísima.
  • E/S por interrupciones: el dispositivo avisa a la CPU mediante una interrupción cuando está listo, liberando al procesador para otras tareas mientras tanto. Mejora notablemente el aprovechamiento del procesador, pero cada palabra o bloque transferido sigue exigiendo la intervención de la CPU y el coste de atender la interrupción, lo que la hace inadecuada para dispositivos muy rápidos.
  • Acceso Directo a Memoria (DMA): un controlador especializado, el controlador de DMA, transfiere bloques de datos directamente entre el periférico y la memoria principal sin intervención de la CPU en cada palabra. La CPU solo configura la transferencia —dirección de origen, destino y tamaño— y recibe una única interrupción al concluir el bloque completo. El DMA es imprescindible para dispositivos de alta velocidad como los discos o las tarjetas de red, pues evita que la CPU sea el cuello de botella de la transferencia.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 780 526" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <text x="390" y="30" text-anchor="middle" font-size="16" font-weight="bold" fill="#0f172a">Técnicas de gestión de la E/S: sondeo, interrupciones y DMA</text> <text x="40" y="62" font-size="12" font-weight="bold" fill="#0f172a">1. E/S programada (sondeo / polling)</text> <rect x="524" y="48" width="236" height="20" rx="10" fill="#fef3c7" stroke="#ea580c" stroke-width="1.2"/> <rect x="534" y="53" width="9" height="9" fill="#ea580c"/> <rect x="547" y="53" width="9" height="9" fill="#ea580c"/> <rect x="560" y="53" width="9" height="9" fill="#ea580c"/> <text x="576" y="62" font-size="10" font-weight="bold" fill="#0f172a">Intervención CPU: ALTA</text> <rect x="40" y="84" width="150" height="62" rx="8" fill="#f8fafc" stroke="#334155" stroke-width="2"/> <text x="115" y="110" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">Controlador</text> <text x="115" y="126" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">de E/S</text> <rect x="360" y="84" width="105" height="62" rx="8" fill="#eff6ff" stroke="#334155" stroke-width="2"/> <text x="412" y="121" text-anchor="middle" font-size="14" font-weight="bold" fill="#0f172a">CPU</text> <rect x="615" y="84" width="145" height="62" rx="8" fill="#f8fafc" stroke="#334155" stroke-width="2"/> <text x="687" y="121" text-anchor="middle" font-size="13" font-weight="bold" fill="#0f172a">Memoria</text> <line x1="360" y1="100" x2="199" y2="100" stroke="#2563eb" stroke-width="1.8"/> <polygon points="190,100 199,96 199,104" fill="#2563eb"/> <text x="275" y="93" text-anchor="middle" font-size="9.5" font-style="italic" fill="#2563eb">consulta en bucle: ¿listo?</text> <line x1="190" y1="126" x2="351" y2="126" stroke="#2563eb" stroke-width="1.8"/> <polygon points="360,126 351,122 351,130" fill="#2563eb"/> <text x="275" y="140" text-anchor="middle" font-size="10" fill="#334155">dato</text> <line x1="465" y1="126" x2="606" y2="126" stroke="#2563eb" stroke-width="1.8"/> <polygon points="615,126 606,122 606,130" fill="#2563eb"/> <text x="540" y="140" text-anchor="middle" font-size="10" fill="#334155">dato</text> <text x="40" y="171" font-size="10.5" font-style="italic" fill="#dc2626">La CPU malgasta ciclos en espera activa.</text> <line x1="30" y1="190" x2="750" y2="190" stroke="#e2e8f0" stroke-width="1"/> <text x="40" y="216" font-size="12" font-weight="bold" fill="#0f172a">2. E/S por interrupciones</text> <rect x="524" y="202" width="236" height="20" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-width="1.2"/> <rect x="534" y="207" width="9" height="9" fill="#2563eb"/> <rect x="547" y="207" width="9" height="9" fill="#2563eb"/> <rect x="560" y="207" width="9" height="9" fill="#ffffff" stroke="#2563eb" stroke-width="1"/> <text x="576" y="216" font-size="10" font-weight="bold" fill="#0f172a">Intervención CPU: MEDIA</text> <rect x="40" y="238" width="150" height="62" rx="8" fill="#f8fafc" stroke="#334155" stroke-width="2"/> <text x="115" y="264" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">Controlador</text> <text x="115" y="280" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">de E/S</text> <rect x="360" y="238" width="105" height="62" rx="8" fill="#eff6ff" stroke="#334155" stroke-width="2"/> <text x="412" y="275" text-anchor="middle" font-size="14" font-weight="bold" fill="#0f172a">CPU</text> <rect x="615" y="238" width="145" height="62" rx="8" fill="#f8fafc" stroke="#334155" stroke-width="2"/> <text x="687" y="275" text-anchor="middle" font-size="13" font-weight="bold" fill="#0f172a">Memoria</text> <line x1="190" y1="254" x2="351" y2="254" stroke="#dc2626" stroke-width="1.8"/> <polygon points="360,254 351,250 351,258" fill="#dc2626"/> <text x="275" y="247" text-anchor="middle" font-size="10" font-weight="bold" fill="#dc2626">INT (listo)</text> <line x1="190" y1="280" x2="351" y2="280" stroke="#2563eb" stroke-width="1.8"/> <polygon points="360,280 351,276 351,284" fill="#2563eb"/> <text x="275" y="294" text-anchor="middle" font-size="10" fill="#334155">dato (palabra)</text> <line x1="465" y1="280" x2="606" y2="280" stroke="#2563eb" stroke-width="1.8"/> <polygon points="615,280 606,276 606,284" fill="#2563eb"/> <text x="540" y="294" text-anchor="middle" font-size="10" fill="#334155">palabra</text> <text x="40" y="325" font-size="10.5" font-style="italic" fill="#64748b">La CPU atiende bajo demanda, pero interviene en cada transferencia.</text> <line x1="30" y1="344" x2="750" y2="344" stroke="#e2e8f0" stroke-width="1"/> <text x="40" y="370" font-size="12" font-weight="bold" fill="#0f172a">3. Acceso Directo a Memoria (DMA)</text> <rect x="524" y="356" width="236" height="20" rx="10" fill="#ecfdf5" stroke="#059669" stroke-width="1.2"/> <rect x="534" y="361" width="9" height="9" fill="#059669"/> <rect x="547" y="361" width="9" height="9" fill="#ffffff" stroke="#059669" stroke-width="1"/> <rect x="560" y="361" width="9" height="9" fill="#ffffff" stroke="#059669" stroke-width="1"/> <text x="576" y="370" font-size="10" font-weight="bold" fill="#0f172a">Intervención CPU: MÍNIMA</text> <rect x="40" y="392" width="150" height="62" rx="8" fill="#ecfdf5" stroke="#059669" stroke-width="2"/> <text x="115" y="418" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">Controlador</text> <text x="115" y="434" text-anchor="middle" font-size="12" font-weight="bold" fill="#0f172a">DMA / E/S</text> <rect x="360" y="392" width="105" height="62" rx="8" fill="#f8fafc" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 3" opacity="0.5"/> <text x="412" y="428" text-anchor="middle" font-size="14" font-weight="bold" fill="#0f172a" opacity="0.5">CPU</text> <rect x="615" y="392" width="145" height="62" rx="8" fill="#f8fafc" stroke="#334155" stroke-width="2"/> <text x="687" y="428" text-anchor="middle" font-size="13" font-weight="bold" fill="#0f172a">Memoria</text> <line x1="360" y1="410" x2="199" y2="410" stroke="#64748b" stroke-width="1.5"/> <polygon points="190,410 199,406 199,414" fill="#64748b"/> <text x="275" y="403" text-anchor="middle" font-size="9.5" font-style="italic" fill="#64748b">configura transferencia</text> <line x1="190" y1="433" x2="351" y2="433" stroke="#dc2626" stroke-width="1.5"/> <polygon points="360,433 351,429 351,437" fill="#dc2626"/> <text x="275" y="450" text-anchor="middle" font-size="9.5" font-style="italic" fill="#dc2626">INT al terminar el bloque</text> <polyline points="115,464 115,480 687,480 687,464" fill="none" stroke="#059669" stroke-width="6"/> <polygon points="115,455 109,464 121,464" fill="#059669"/> <polygon points="687,455 681,464 693,464" fill="#059669"/> <text x="401" y="497" text-anchor="middle" font-size="10.5" font-weight="bold" fill="#059669">transferencia directa de bloques (DMA)</text> <text x="40" y="514" font-size="10.5" font-style="italic" fill="#64748b">La CPU queda libre; el DMA mueve bloques enteros.</text> </svg>

Figura 9. Las tres tecnicas de gestion de la E/S: sondeo, interrupciones y DMA.

El DMA admite, a su vez, distintos modos de operación según cómo comparta el bus con la CPU. En el modo por ráfagas (burst), el controlador de DMA toma el bus y transfiere el bloque entero de una vez, maximizando el rendimiento pero bloqueando a la CPU durante ese lapso. En el modo por robo de ciclo (cycle stealing), intercala transferencias de una palabra entre los accesos de la CPU, ralentizándola mínimamente. Y en el modo transparente, solo transfiere cuando la CPU no necesita el bus, sin penalizarla en absoluto pero a menor velocidad. Existe además un fenómeno a vigilar: como el DMA escribe directamente en memoria, puede dejar la caché con datos obsoletos, por lo que el sistema debe garantizar la coherencia entre la memoria actualizada por DMA y las cachés, ya sea por hardware o invalidando explícitamente las líneas afectadas.

8.3. Mapeado de la E/S

Para que la CPU dialogue con los controladores existen dos esquemas. En la E/S mapeada en memoria (memory-mapped I/O), los registros de los controladores ocupan posiciones del mismo espacio de direcciones que la memoria, de modo que se accede a ellos con las instrucciones ordinarias de carga y almacenamiento; es el esquema de las arquitecturas ARM y RISC-V, y su gran ventaja es la uniformidad, pues no requiere instrucciones especiales y permite aplicar a los periféricos todo el repertorio de operaciones de memoria. En la E/S por puertos aislados (port-mapped I/O), los dispositivos disponen de un espacio de direcciones propio, separado del de memoria, y se accede a ellos con instrucciones específicas (IN y OUT en x86); su ventaja es no consumir espacio de direcciones de memoria, relevante cuando este era escaso.

Las interfaces físicas modernas más relevantes incluyen USB para periféricos de uso general (con velocidades que van desde 1,5 Mbit/s del USB de baja velocidad hasta los 40 Gbit/s de USB4), PCI Express como interconexión interna de expansión de alta velocidad, SATA y NVMe para almacenamiento, HDMI o DisplayPort para vídeo, y Thunderbolt como interfaz combinada de datos y vídeo. Un matiz relevante es que las operaciones de E/S, al ser potencialmente peligrosas, están reservadas al modo privilegiado: un programa de usuario que necesita acceder a un dispositivo debe hacerlo a través de una llamada al sistema, lo que preserva el aislamiento y la protección.

9. El sistema de interconexión: buses de datos, direcciones y control

Los subsistemas descritos no operan aislados, sino que intercambian información a través del sistema de interconexión, tradicionalmente concebido como un conjunto de buses. Un bus es un conjunto de líneas conductoras compartidas que transportan información entre componentes según un protocolo común. La metáfora clásica del bus del sistema distingue tres buses según la naturaleza de lo que transportan.

9.1. Los tres buses funcionales

  • Bus de datos: transporta la información propiamente dicha —instrucciones y datos— entre la CPU, la memoria y la E/S. Es bidireccional. Su anchura (número de líneas, equivalente al tamaño de palabra: 8, 16, 32 o 64 bits) determina cuántos bits se transfieren simultáneamente y condiciona directamente el rendimiento.
  • Bus de direcciones: transporta la dirección de la posición de memoria o del puerto de E/S a la que se desea acceder. Es unidireccional (de la CPU hacia memoria y E/S). Su anchura determina el espacio de direccionamiento máximo: con n líneas se pueden direccionar 2^{n} posiciones. Así, un bus de direcciones de 32 bits direcciona 4 gibibytes, mientras que uno de 48 o 64 bits sustenta los enormes espacios virtuales actuales.
  • Bus de control: transporta las señales de gobierno que coordinan las transferencias: señales de lectura y escritura, de reloj, de petición y reconocimiento de bus, de interrupción, de petición de DMA, etc. Determina el sentido y el momento de cada operación.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 740 420" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <text x="370" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#0f172a">El sistema de tres buses</text> <!-- Cajas superiores --> <rect x="200" y="46" width="150" height="48" rx="6" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="275" y="75" text-anchor="middle" font-size="14" font-weight="700" fill="#0f172a">CPU</text> <rect x="370" y="46" width="150" height="48" rx="6" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="445" y="75" text-anchor="middle" font-size="13" font-weight="700" fill="#0f172a">Memoria principal</text> <rect x="540" y="46" width="150" height="48" rx="6" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="615" y="71" text-anchor="middle" font-size="12.5" font-weight="700" fill="#0f172a">E/S</text> <text x="615" y="86" text-anchor="middle" font-size="11" fill="#64748b">(controladores)</text> <!-- Conexiones verticales de cada caja a los tres buses --> <!-- CPU x=275 --> <line x1="275" y1="94" x2="275" y2="150" stroke="#334155" stroke-width="1"/> <line x1="275" y1="94" x2="275" y2="230" stroke="#334155" stroke-width="1"/> <line x1="275" y1="94" x2="275" y2="310" stroke="#334155" stroke-width="1"/> <!-- Memoria x=445 --> <line x1="445" y1="94" x2="445" y2="150" stroke="#334155" stroke-width="1"/> <line x1="445" y1="94" x2="445" y2="230" stroke="#334155" stroke-width="1"/> <line x1="445" y1="94" x2="445" y2="310" stroke="#334155" stroke-width="1"/> <!-- E/S x=615 --> <line x1="615" y1="94" x2="615" y2="150" stroke="#334155" stroke-width="1"/> <line x1="615" y1="94" x2="615" y2="230" stroke="#334155" stroke-width="1"/> <line x1="615" y1="94" x2="615" y2="310" stroke="#334155" stroke-width="1"/> <!-- Puntos de conexion (pequenos cuadrados en cruces) --> <rect x="271" y="146" width="8" height="8" fill="#2563eb"/> <rect x="441" y="146" width="8" height="8" fill="#2563eb"/> <rect x="611" y="146" width="8" height="8" fill="#2563eb"/> <rect x="271" y="226" width="8" height="8" fill="#334155"/> <rect x="441" y="226" width="8" height="8" fill="#334155"/> <rect x="611" y="226" width="8" height="8" fill="#334155"/> <rect x="271" y="306" width="8" height="8" fill="#334155"/> <rect x="441" y="306" width="8" height="8" fill="#334155"/> <rect x="611" y="306" width="8" height="8" fill="#334155"/> <!-- ===================== BUS DE DATOS (y=150) bidireccional, grueso, azul ===================== --> <text x="20" y="146" font-size="13" font-weight="700" fill="#2563eb">Bus de datos</text> <!-- linea principal entre flechas --> <line x1="186" y1="150" x2="704" y2="150" stroke="#2563eb" stroke-width="3.5"/> <!-- flecha izquierda --> <polygon points="170,150 188,142 188,158" fill="#2563eb"/> <!-- flecha derecha --> <polygon points="720,150 702,142 702,158" fill="#2563eb"/> <text x="20" y="162" font-size="10" fill="#64748b">bidireccional</text> <!-- nota a la derecha --> <rect x="540" y="166" width="184" height="22" rx="4" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="548" y="181" font-size="10.5" fill="#0f172a">instrucciones y datos (8/16/32/64 bits)</text> <!-- ===================== BUS DE DIRECCIONES (y=230) unidireccional CPU -> ===================== --> <text x="20" y="226" font-size="13" font-weight="700" fill="#0f172a">Bus de direcciones</text> <!-- linea sale de CPU (x=275) hacia la derecha hasta E/S --> <line x1="275" y1="230" x2="702" y2="230" stroke="#334155" stroke-width="2"/> <!-- flecha unica al final (derecha) --> <polygon points="720,230 702,222 702,238" fill="#334155"/> <!-- pequena flecha intermedia hacia memoria para indicar destino --> <polygon points="445,230 431,224 431,236" fill="#334155"/> <text x="20" y="242" font-size="10" fill="#64748b">unidireccional</text> <!-- nota --> <rect x="500" y="246" width="224" height="22" rx="4" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="508" y="261" font-size="10.5" fill="#0f172a">selecciona posición (2&#8319; posiciones)</text> <!-- ===================== BUS DE CONTROL (y=310) ambos sentidos, finas ===================== --> <text x="20" y="306" font-size="13" font-weight="700" fill="#0f172a">Bus de control</text> <!-- linea principal fina --> <line x1="188" y1="310" x2="702" y2="310" stroke="#334155" stroke-width="1"/> <!-- flechas finas en ambos extremos --> <polygon points="172,310 188,304 188,316" fill="#334155"/> <polygon points="718,310 702,304 702,316" fill="#334155"/> <text x="20" y="322" font-size="10" fill="#64748b">ambos sentidos</text> <!-- nota --> <rect x="490" y="326" width="234" height="22" rx="4" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/> <text x="498" y="341" font-size="10.5" fill="#0f172a">lectura/escritura, reloj, interrupciones</text> <!-- Leyenda inferior --> <line x1="20" y1="372" x2="720" y2="372" stroke="#e2e8f0" stroke-width="1"/> <text x="20" y="392" font-size="11" fill="#64748b">Los tres buses interconectan la CPU, la memoria y los controladores de E/S.</text> <text x="20" y="408" font-size="11" fill="#64748b">El de datos (azul, grueso) es bidireccional; el de direcciones, unidireccional desde la CPU.</text> </svg>

Figura 10. El sistema de tres buses: datos (bidireccional), direcciones (unidireccional) y control.

\text{Espacio direccionable} = 2^{n}\ \text{posiciones} \qquad (n=32 \Rightarrow 2^{32} = 4\ \text{GiB}).

9.2. Parámetros y temporización

El rendimiento de un bus depende de su anchura y de su frecuencia de funcionamiento; el producto de ambas da el ancho de banda (bytes por segundo transferibles). Por ejemplo, un bus de 64 bits (8 bytes) operando a 1.600 MHz proporciona un ancho de banda de 8 \times 1{,}6\times10^{9} = 12{,}8 GB/s. El acceso al bus exige un protocolo de temporización, que puede ser síncrono (gobernado por un reloj común, sencillo pero limitado por el dispositivo más lento y por la longitud de las líneas) o asíncrono (basado en señales de petición y reconocimiento, handshaking, más flexible y tolerante a dispositivos de velocidades dispares). Cuando varios maestros (CPU, controladores de DMA) pueden iniciar transferencias, se necesita un árbitro de bus que conceda el acceso y evite conflictos, mediante esquemas de prioridad fija, prioridad rotatoria (round-robin) o encadenamiento en margarita (daisy chain).

9.3. De los buses compartidos a las interconexiones punto a punto

El modelo de bus compartido único, válido para razonar conceptualmente, ha quedado superado en la práctica por sus limitaciones: un medio compartido degrada su rendimiento al crecer el número de dispositivos y la frecuencia, por problemas de capacitancia parásita, de desfase entre líneas (skew) y de contención por el acceso. Las arquitecturas actuales han evolucionado hacia enlaces serie punto a punto de alta velocidad y hacia topologías conmutadas. PCI Express, por ejemplo, no es un bus compartido sino un conjunto de enlaces serie diferenciales (lanes) conmutados; un dispositivo puede agrupar 1, 4, 8 o 16 lanes según su necesidad de ancho de banda. La evolución de sus generaciones ilustra bien la tendencia, pues cada una duplica aproximadamente la tasa por lane:

Generación PCIe Tasa por lane Ancho de banda x16 (aprox., por sentido)
PCIe 3.0 8 GT/s ≈ 16 GB/s
PCIe 4.0 16 GT/s ≈ 32 GB/s
PCIe 5.0 32 GT/s ≈ 63 GB/s
PCIe 6.0 64 GT/s (PAM4) ≈ 121 GB/s

Las interconexiones entre núcleos y memoria en el interior del propio chip emplean redes en anillo o en malla, y entre procesadores se usan enlaces coherentes como los de las familias de servidores. Conceptualmente, no obstante, sigue siendo válido distinguir las tres funciones —transportar datos, direcciones y control—, aunque físicamente viajen multiplexadas y serializadas sobre los mismos enlaces diferenciales, con la dirección y los datos empaquetados en tramas con encabezado. El esquema funcional clásico del bus tripartito conserva, por ello, todo su valor pedagógico como abstracción, aunque no describa ya la realidad eléctrica.

10. Generaciones y evolución de los ordenadores digitales

La historia del ordenador digital se ha organizado tradicionalmente en generaciones, definidas por la tecnología dominante de sus componentes activos. Esta periodización, aunque simplificadora, resulta útil para comprender la evolución de los elementos funcionales.

  • Generación cero (electromecánica, hasta los años cuarenta): máquinas basadas en relés, como el Harvard Mark I o el Z3 de Konrad Zuse (1941), considerado el primer ordenador programable controlado por programa. En este periodo se gestan las ideas, pero las máquinas son lentas y de propósito limitado.
  • Primera generación (válvulas de vacío, 1945-1955): el ENIAC (1946), de propósito general pero programado por recableado, y el EDVAC y el EDSAC (1949), ya con programa almacenado, materializan el modelo de von Neumann. Son máquinas voluminosas, costosas y poco fiables, programadas en lenguaje máquina.
  • Segunda generación (transistor, 1955-1965): la invención del transistor en 1947 (Bardeen, Brattain y Shockley, en los Laboratorios Bell) sustituye a la válvula, reduciendo tamaño, consumo y fallos. Aparecen los primeros lenguajes de alto nivel (FORTRAN, COBOL, LISP) y los sistemas operativos por lotes.
  • Tercera generación (circuito integrado, 1965-1971): la integración de múltiples transistores en un mismo chip (Jack Kilby y Robert Noyce) reduce drásticamente el coste y el tamaño. Surgen las familias de computadores compatibles, como el IBM System/360, que separa por primera vez arquitectura de implementación, y la multiprogramación.
  • Cuarta generación (microprocesador, desde 1971): la integración a gran escala permite alojar toda la CPU en un solo chip. El Intel 4004 (1971), de 4 bits y unos 2.300 transistores, inaugura la era del microprocesador, que conduce al ordenador personal y a la informática ubicua. Es la generación en la que seguimos, ampliada por los procesadores multinúcleo, la computación móvil y los sistemas en chip (SoC).
  • Más allá de la cuarta generación: suele hablarse de una quinta generación asociada históricamente a la inteligencia artificial y al procesamiento paralelo masivo. Hoy las fronteras de la evolución se sitúan en los aceleradores especializados (GPU para cómputo masivamente paralelo, TPU y NPU para inteligencia artificial), la computación heterogénea, los sistemas en chip que integran CPU, GPU y aceleradores, y, en el horizonte de la investigación, la computación cuántica y la neuromórfica, paradigmas que se apartan del modelo de von Neumann y abren cuestiones teóricas sobre los límites de lo computable de forma eficiente.

10.1. Las leyes de escalado y sus límites

El motor de toda esta evolución ha sido la ley de Moore, la observación de Gordon Moore (1965) de que el número de transistores por chip se duplicaba aproximadamente cada dos años. Íntimamente ligado a ella operó durante décadas el escalado de Dennard, que establecía que al reducir el tamaño de los transistores su densidad de potencia se mantenía constante, de modo que cada nueva generación ofrecía más transistores a mayor frecuencia sin aumentar el consumo por unidad de área. La combinación de ambas leyes sostuvo el crecimiento exponencial del rendimiento durante casi cuatro décadas.

Sin embargo, hacia mediados de la década de 2000 el escalado de Dennard se rompió: a escalas nanométricas, las corrientes de fuga y la disipación térmica dejaron de ser despreciables, y la frecuencia de reloj se estancó en torno a los pocos gigahercios, incapaz de crecer sin fundir el chip. Es el llamado muro de la potencia (power wall). La respuesta de la industria fue histórica: dejar de perseguir la frecuencia y volcarse en el paralelismo, multiplicando los núcleos en lugar de acelerarlos. A ello se sumó el fenómeno del silicio oscuro (dark silicon), esto es, la imposibilidad de mantener encendidos simultáneamente todos los transistores de un chip por restricciones térmicas, que ha impulsado la especialización: en vez de núcleos genéricos siempre activos, se integran bloques especializados (codificadores de vídeo, motores de IA, criptografía) que se encienden solo cuando se necesitan y hacen su tarea con enorme eficiencia energética.

La ley de Moore, aun ralentizada, no se ha detenido del todo, pero se sostiene con nuevas técnicas: transistores tridimensionales (FinFET y, más recientemente, nanosheet o GAAFET), apilamiento vertical de circuitos (integración 3D) y, muy señaladamente, el diseño con chiplets, que fragmenta un procesador en varios dados más pequeños fabricados por separado y unidos en un mismo encapsulado, mejorando el rendimiento de fabricación y permitiendo mezclar tecnologías. En el panorama de 2026, esta tendencia se manifiesta en los sistemas en chip de alto rendimiento y bajo consumo basados en ARM que dominan la computación móvil y ganan terreno en portátiles y servidores, en el auge de RISC-V como alternativa abierta, y en la centralidad de las GPU y NPU para las cargas de inteligencia artificial. Todo ello confirma que la mejora del rendimiento se busca hoy menos en la frecuencia y más en el paralelismo, la eficiencia energética y la especialización del hardware, lo que a su vez pone bajo tensión el modelo clásico: la computación centrada en datos, el procesamiento en memoria (in-memory computing) o los aceleradores desagregados son intentos de sortear el cuello de botella de von Neumann sin abandonar, salvo en paradigmas experimentales como el cuántico o el neuromórfico, el modelo conceptual heredado de von Neumann y Turing.

11. Aplicación didáctica y relación con el currículo

Los contenidos de este tema constituyen el núcleo conceptual de varios módulos de la Formación Profesional de la familia Informática y Comunicaciones, regulada por la Ley Orgánica 3/2022, de ordenación e integración de la Formación Profesional (LOFP) y su desarrollo en el Real Decreto 659/2023, que ordena el nuevo sistema de FP. En el CFGM de Sistemas Microinformáticos y Redes se imparten en módulos como «Montaje y Mantenimiento de Equipos», donde el alumnado identifica físicamente la CPU, la memoria, los buses y los dispositivos de E/S, y comprende la jerarquía de memoria al seleccionar componentes con criterios de rendimiento y coste. En los ciclos superiores —Desarrollo de Aplicaciones Multiplataforma, Desarrollo de Aplicaciones Web y Administración de Sistemas Informáticos en Red— estos fundamentos sustentan módulos como «Sistemas Informáticos» y «Programación», pues comprender el ciclo de instrucción, los registros y la gestión de memoria resulta imprescindible para escribir software eficiente, dimensionar servidores y administrar sistemas.

En la ESO y el Bachillerato, dentro del marco de la Ley Orgánica 2/2006 de Educación (LOE) modificada por la LOMLOE 3/2020, materias como «Tecnología y Digitalización» y «Digitalización» abordan, con un enfoque competencial y adaptado a la edad, el conocimiento básico del hardware, el funcionamiento del ordenador y la representación digital de la información, contribuyendo a la competencia digital del alumnado, una de las competencias clave del perfil de salida. En la Comunitat Valenciana, este marco se concreta en los decretos autonómicos de currículo que despliegan la LOMLOE y en la normativa de la Conselleria competente en educación que regula los ciclos formativos de la familia Informática y Comunicaciones.

Desde el punto de vista metodológico, conviene partir del modelo funcional sencillo —los cuatro subsistemas y los tres buses— para construir progresivamente la complejidad, evitando que el alumnado memorice piezas sin entender su cooperación. Resultan muy eficaces los simuladores de CPU y de ciclo de instrucción, que permiten visualizar el flujo fetch-decode-execute y observar cómo cambian los registros paso a paso; las prácticas de montaje y desmontaje de equipos reales, que anclan lo abstracto en lo tangible; y los proyectos con placas basadas en microcontroladores didácticos, que materializan la arquitectura Harvard y conectan la teoría con el internet de las cosas. El aprendizaje por proyectos y la resolución de situaciones de aprendizaje contextualizadas, propios del enfoque competencial LOMLOE, encajan de manera natural con estos contenidos. La evaluación debe ser formativa y combinar pruebas conceptuales con la resolución de supuestos prácticos y la elaboración de informes técnicos, de acuerdo con los resultados de aprendizaje y criterios de evaluación que fijan los reales decretos de cada título. Este enfoque, que vincula el rigor científico con la práctica profesional, es coherente con el perfil de la familia profesional y con las demandas del sector productivo.

12. Conclusión

A lo largo del tema hemos recorrido los elementos funcionales del ordenador digital partiendo de su fundamento teórico y descendiendo hasta su realización física. La máquina de Turing nos ha proporcionado el marco que define qué es computable, ha revelado que la inmensa mayoría de las funciones son incomputables y ha mostrado que el ordenador moderno es, en esencia, una realización de la máquina universal: una máquina que lee la descripción de otra y la ejecuta. La arquitectura de von Neumann, con su principio de programa almacenado, ha traducido esa idea en una organización concreta —CPU, memoria, E/S y buses— cuya vigencia se mantiene ochenta años después, mientras la arquitectura Harvard y sus variantes modificadas demuestran cómo el ingenio sortea las limitaciones del modelo original —singularmente el cuello de botella de von Neumann— sin renunciar a su flexibilidad.

Hemos analizado la CPU y sus tres bloques —unidad de control, ALU y registros—, con sus dicotomías RISC/CISC y microarquitectura/ISA; el ciclo de instrucción como latido del sistema, cuantificado mediante la ecuación del rendimiento y refinado con segmentación y paralelismo bajo el techo que impone la ley de Amdahl; la jerarquía de memoria sustentada en el principio de localidad, cuya eficacia hemos medido con el tiempo medio de acceso; el subsistema de E/S con sus técnicas de sondeo, interrupciones y DMA; y el sistema de buses que articula el conjunto, ya evolucionado hacia interconexiones serie punto a punto. La perspectiva histórica de las generaciones, y el análisis de la ley de Moore, el escalado de Dennard y sus límites, ha mostrado que, aunque la tecnología cambie de las válvulas a los nanómetros, los elementos funcionales y su lógica de cooperación permanecen como invariantes conceptuales.

La idea de fondo que conviene retener, y transmitir al alumnado, es que el ordenador no es una suma de piezas, sino un sistema cuyo comportamiento emerge de la interacción coordinada de unidades simples gobernadas por un programa. Comprender esta arquitectura no solo es requisito para administrar, programar o reparar equipos, sino que constituye la base intelectual de toda la disciplina informática y el punto de partida natural para abordar los temas posteriores del temario, desde la representación de la información hasta los sistemas operativos y las redes.

13. Bibliografía y referencias

(a) Referencias técnicas (manuales y estándares de referencia)

  • Stallings, W. (2021). Organización y arquitectura de computadores (11.ª ed.). Pearson.
  • Hennessy, J. L. y Patterson, D. A. (2019). Computer Architecture: A Quantitative Approach (6.ª ed.). Morgan Kaufmann.
  • Patterson, D. A. y Hennessy, J. L. (2021). Computer Organization and Design: The Hardware/Software Interface (RISC-V Edition, 2.ª ed.). Morgan Kaufmann.
  • Tanenbaum, A. S. y Austin, T. (2013). Organización de computadoras: estructura de los sistemas digitales (6.ª ed.). Pearson.
  • Hyde, R. (2010). The Art of Assembly Language (2.ª ed.). No Starch Press.
  • Turing, A. M. (1936). «On Computable Numbers, with an Application to the Entscheidungsproblem». Proceedings of the London Mathematical Society.
  • von Neumann, J. (1945). First Draft of a Report on the EDVAC. Moore School of Electrical Engineering, University of Pennsylvania.
  • Backus, J. (1978). «Can Programming Be Liberated from the von Neumann Style?». Communications of the ACM, 21(8).
  • Sipser, M. (2012). Introduction to the Theory of Computation (3.ª ed.). Cengage Learning.
  • Hopcroft, J. E., Motwani, R. y Ullman, J. D. (2007). Introduction to Automata Theory, Languages, and Computation (3.ª ed.). Pearson.
  • IEEE Standard for Floating-Point Arithmetic, IEEE 754-2019.
  • JEDEC Solid State Technology Association. DDR5 SDRAM Standard (JESD79-5).
  • PCI-SIG. PCI Express Base Specification (vigente).
  • Webgrafía: documentación técnica de arquitecturas en developer.arm.com, riscv.org y www.intel.com (consultadas en 2026).

(b) Referencias normativas

  • Ley Orgánica 2/2006, de 3 de mayo, de Educación (LOE), modificada por la Ley Orgánica 3/2020, de 29 de diciembre (LOMLOE).
  • Ley Orgánica 3/2022, de 31 de marzo, de ordenación e integración de la Formación Profesional (LOFP).
  • Real Decreto 659/2023, de 18 de julio, por el que se desarrolla la ordenación del Sistema de Formación Profesional.
  • Real Decreto 1691/2007, por el que se establece el título de Técnico en Sistemas Microinformáticos y Redes.
  • Reales Decretos de los títulos de Técnico Superior en Desarrollo de Aplicaciones Multiplataforma, Desarrollo de Aplicaciones Web y Administración de Sistemas Informáticos en Red.
  • Orden de 1 de febrero de 1996 (BOE) por la que se aprueban los temarios de las especialidades del Cuerpo de Profesores de Enseñanza Secundaria, especialidad Informática.
  • Normativa autonómica de la Comunitat Valenciana de desarrollo curricular de los ciclos formativos de la familia Informática y Comunicaciones.

Orientaciones para el estudio

  • Domina primero el esquema de los cuatro subsistemas y los tres buses; es el andamiaje sobre el que cuelga todo lo demás y permite reconstruir el tema aunque se olvide algún detalle.
  • Memoriza el ciclo de instrucción con sus registros (PC, IR, MAR, MDR) y sé capaz de narrarlo paso a paso, incluso en notación RTL: es la pregunta estrella y demuestra comprensión profunda ante el tribunal.
  • Lleva preparado al menos un ejemplo numérico resuelto de cada bloque (el AMAT con dos niveles de caché, el desglose de una dirección en etiqueta/índice/desplazamiento, la ecuación del rendimiento, la ley de Amdahl); un cálculo bien ejecutado eleva la nota más que un párrafo teórico.
  • Practica la comparación von Neumann frente a Harvard con un ejemplo concreto (un PC frente a un microcontrolador) y cuantifica el ancho de banda; las comparaciones bien argumentadas y con números elevan la nota.
  • Ten claras las dicotomías que suelen preguntarse: RISC/CISC, acarreo/desbordamiento, ISA/microarquitectura, escritura directa/posescritura, sondeo/interrupciones/DMA. Distinguirlas con precisión separa un 7 de un 10.
  • Conecta cada concepto técnico con su utilidad práctica y didáctica; el tribunal valora que un futuro docente sepa para qué sirve lo que explica y cómo lo enseñaría.
  • Dibuja siempre un esquema de la arquitectura al desarrollar el tema por escrito: ordena tu exposición, ahorra palabras y transmite claridad.
  • Actualiza los ejemplos a 2026 (DDR5, PCIe 5.0/6.0, NVMe, RISC-V, chiplets, GPU/NPU, fin del escalado de Dennard) para evidenciar rigor técnico vigente sin perder los fundamentos atemporales.

Genera tus temas como este

Crea temas desarrollados de tu especialidad y comunidad, con el tono y la longitud que prefieras, y conviértelos en esquemas y flashcards.