01 · Punto de partida
Memoria que no se olvida
Una ROM comparte con la RAM (tema 26) la organización de palabras y direcciones, y se lee con las mismas señales de dirección, datos y habilitación. Se diferencia en dos cosas:
- No tiene entrada de escritura (no existe WE̅): el procesador no puede cambiar su contenido en funcionamiento normal.
- Es no volátil: el contenido sobrevive sin alimentación, durante años.
Su rol natural es guardar lo que no cambia o cambia raramente: el firmware (el programa que arranca el equipo), las tablas de constantes, las fuentes de caracteres, el código de un videojuego de cartucho. Apenas se enciende una computadora, lo primero que ejecuta está en una ROM, porque la RAM todavía está vacía.
| Aspecto | RAM | ROM |
|---|---|---|
| Lectura | Sí | Sí |
| Escritura en funcionamiento | Sí, con WE̅ | No |
| Sin alimentación | Pierde el contenido | Lo conserva |
| Contenido al encender | Indefinido | El que se programó |
| Uso típico | Variables y datos de trabajo | Programa de arranque, constantes, tablas |
02 · Estructura
La ROM por dentro: una tabla de verdad
Una ROM de n líneas de dirección y m de datos tiene dos bloques. Un decodificador de n a 2n activa, para cada dirección, una única línea de palabra. Una matriz de m columnas conecta cada línea de palabra con las columnas cuyo bit vale 1 (con un diodo o un transistor en el cruce); las columnas tienen una resistencia a masa, de modo que una columna sin conexión lee 0.
Cada punto es una conexión programada. Leer la dirección 01 (palabra W1) activa su línea; los puntos de D1 y D0 la conectan con esas columnas, que suben a 1, y D2 queda en 0: el dato es 011. La matriz es el contenido. Programar una ROM es decidir dónde hay conexión y dónde no.
Una ROM implementa cualquier función combinacional
Mirá la ROM de otra manera: las líneas de dirección son las entradas de un circuito combinacional y las columnas de datos son sus salidas. El decodificador genera todos los minitérminos de las entradas; la matriz es un conjunto de compuertas OR que suma los que correspondan a cada salida. Con la ROM de arriba:
| A1 | A0 | D2 | D1 | D0 |
|---|---|---|---|---|
| 0 | 0 | 1 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 1 | 1 | 0 |
| 1 | 1 | 0 | 0 | 0 |
D2 = ~A1·~A0 + A1·~A0 = ~A0; D1 = ~A1·A0 + A1·~A0 = A1 ⊕ A0; D0 = ~A1·~A0 + ~A1·A0 = ~A1. No hizo falta simplificar nada: se copió la tabla de verdad. Esa es la gran ventaja de implementar lógica con una ROM: el diseño es la tabla, y cambiar la función es cambiar los datos, no el cableado.
03 · Tipos
ROM de máscara, PROM y EPROM
Lo que distingue a las ROM es cuándo y cómo se fija el contenido:
| Tipo | Cómo se programa | ¿Se puede cambiar? | Cuándo conviene |
|---|---|---|---|
| ROM de máscara | En la fábrica, con una máscara del proceso que define las conexiones | No | Producción masiva de un contenido ya definitivo (costo inicial alto, costo por chip muy bajo) |
| PROM (Programmable ROM) | Con un programador: se «queman» fusibles o anti-fusibles | No: una sola vez (OTP, one-time programmable) | Series chicas y medianas con contenido definitivo |
| EPROM (Erasable PROM) | Con un programador, aplicando una tensión alta | Sí: se borra con luz ultravioleta y se reprograma | Desarrollo y prototipos, donde el contenido cambia |
| EEPROM y Flash | Eléctricamente, en el propio circuito | Sí, eléctricamente | Temas 29 y 30 |
PROM: el fusible
En una PROM cada cruce de la matriz tiene una conexión con un fusible. De fábrica, todos los fusibles están intactos y todas las palabras valen 1 (o 0, según la tecnología). El programador hace pasar una corriente intensa por los fusibles de los bits que deben cambiar y los quema. Un fusible quemado no se repara: el chip es de un solo uso.
EPROM: la compuerta flotante
La celda de una EPROM es un transistor con una compuerta flotante, una isla de silicio aislada por óxido. Para programarla, el programador aplica una tensión alta (VPP, de unos 12 a 21 V) y fuerza electrones calientes a atravesar el óxido y quedar atrapados en la isla: la carga atrapada cambia el umbral del transistor, y esa celda se lee como 0. El aislante es tan bueno que la carga se conserva por más de una década. Para borrarla hace falta darles energía a los electrones para que salgan: se ilumina el chip con luz ultravioleta (unos 254 nm) durante 15 a 20 minutos a través de una ventana de cuarzo que tiene el encapsulado. Por eso se las reconoce a simple vista: una ventanita transparente sobre el chip, que se tapa con una etiqueta para que la luz del ambiente no la borre de a poco.
Las EPROM sin ventana (OTP EPROM) tienen la misma celda pero en un encapsulado plástico barato: no se pueden borrar y se comportan como PROM.
04 · Programación
Programar, borrar y leer
Hay una regla que explica casi todo el comportamiento de estas memorias: programar solo lleva bits de 1 a 0; borrar los devuelve a 1.
- Una EPROM virgen o recién borrada contiene 0xFF en todas las posiciones (todos los bits en 1).
- Programar una palabra con el dato D deja en esa palabra el resultado de (contenido anterior) AND D: los bits de D en 0 se ponen a 0; los bits de D en 1 dejan el bit como estaba.
- Convertir un 0 en 1 es imposible por programación: hay que borrar toda la memoria con UV, no una palabra suelta.
| Contenido anterior | Dato a programar | Resultado (AND) | ¿Lo esperado? |
|---|---|---|---|
| 1111 (virgen) | 1010 | 1010 | Sí |
| 1010 | 1111 | 1010 | No se «reescribe»: los 0 no vuelven a 1 |
| 1010 | 0110 | 0010 | No: el segundo bit pedido en 1 quedó en 0 |
| 1010 | 1000 | 1000 | Sí (solo se pidieron más ceros) |
Antes de programar, el procedimiento correcto es un chequeo de borrado (blank check): comprobar que todas las palabras son 0xFF. Después, programar y verificar leyendo todo y comparando con el original.
Leer
La lectura es la de cualquier memoria asíncrona: se pone la dirección, se activan CE̅ (selección del chip) y OE̅ (salida), y después del tiempo de acceso (decenas a cientos de ns) aparece el dato. No hay WE̅ ni contención posible con el procesador: el único que maneja el bus durante una lectura es la memoria. Los pines extra (VPP y PGM, o como se llamen en el chip) sirven solo para programar; en funcionamiento normal se conectan a +5 V. Un 27C256 tiene 15 líneas de dirección y 8 de datos: 32 768 palabras de 8 bits (tema 26).
05 · Tablas de consulta
Tablas de consulta
Una tabla de consulta (LUT, lookup table) reemplaza un cálculo por una lectura: en vez de calcular f(x), se guarda f(x) para todos los valores posibles de x y se lee la dirección x. Es el mismo compromiso que conoce cualquier programador entre memoria y tiempo de cómputo.
| Aplicación | Entradas (dirección) | Salidas (dato) | Tamaño |
|---|---|---|---|
| Decodificador a siete segmentos (tema 20) | Dígito BCD o hexadecimal de 4 bits | Segmentos a b c d e f g | 16 × 7 bits |
| Generador de caracteres | Código de letra + fila del carácter | Puntos encendidos de esa fila | p. ej. 128 letras × 8 filas × 5 bits |
| Onda senoidal para un DAC | Contador de fase de n bits | Valor de la señal en esa fase | 2n × 8 bits |
| Conversión de códigos (BCD ↔ binario, Gray, etc.) | Código de entrada | Código de salida | 2entradas × salidas |
| Linealización de un sensor | Lectura del conversor A/D | Valor corregido (temperatura, presión) | 256 o 1024 palabras |
| Unidad de control microprogramada | Código de instrucción + estado | Señales de control | Centenares de palabras anchas |
| Aritmética sin circuito | Dos operandos juntos | Resultado | 22n × (n + 1) bits |
Ejemplo: generar una senoide
Un contador de 6 bits recorre las direcciones 0 a 63 de una ROM que contiene 64 muestras de una onda seno, de 8 bits cada una. La salida de la ROM va a un conversor digital-analógico y se obtiene una senoide. Si el reloj del contador es de 64 kHz, la frecuencia de la señal es 64 000 / 64 = 1 kHz. Cambiar el reloj cambia la frecuencia; cambiar el contenido, la forma de onda (cuadrada, triangular, diente de sierra o cualquier otra). Un generador de funciones sencillo es solo un contador, una ROM y un DAC.
06 · Límites
Cuánto se puede meter en una ROM
Una ROM con n entradas tiene 2n palabras: el tamaño crece exponencialmente con el número de entradas. Implementar un sumador de dos números de n bits requiere 2n entradas y n + 1 salidas:
| n | Entradas | Palabras | Bits | Alternativa |
|---|---|---|---|---|
| 2 | 4 | 16 | 48 | Unas pocas compuertas |
| 4 | 8 | 256 | 1280 | Un 74HC283 |
| 6 | 12 | 4096 | 28 672 | Dos 74HC283 en cascada |
| 8 | 16 | 65 536 | 589 824 | Dos 74HC283 (ocho sumadores completos) |
Para un sumador de 8 bits ya serían casi 600 000 bits, cuando bastan ocho sumadores completos. Regla práctica: la ROM conviene cuando hay pocas entradas (hasta unas 10 a 12) y muchas salidas o una función irregular que no se simplifica (un decodificador de siete segmentos, un conversor de código, una tabla de senos). Cuando la función es regular (sumar, comparar, contar), la lógica dedicada gana por mucho.
Las LUT de las FPGA
Las FPGA, que veremos más adelante en tu camino, están construidas sobre esta misma idea: cada bloque lógico contiene una pequeña tabla de consulta de 4 a 6 entradas (una memoria de 16 a 64 bits) que se programa con la tabla de verdad de la función deseada. Una FPGA es, en el fondo, un mar de ROM minúsculas conectadas entre sí.
Entre la ROM y las compuertas sueltas están los dispositivos de lógica programable de matriz fija (PAL, GAL, PLA), que programan la parte AND y/o la parte OR con menos desperdicio. Pero ninguno es tan fácil de entender como la ROM: la tabla de verdad es el programa.
07 · Integridad
Verificar el contenido
Una ROM puede tener un contenido equivocado por un error de programación, un bit que se descargó con los años o una copia defectuosa. Para detectarlo, el firmware lleva junto a los datos un código de verificación:
- Checksum (suma de verificación): se suman todos los bytes módulo 256 y se guarda el complemento; al sumar todo el contenido más ese byte, el resultado debe ser 0. Es muy barato, pero débil: no ve dos errores que se compensen ni bytes intercambiados de lugar.
- CRC (cyclic redundancy check): divide el contenido, tratado como un polinomio, por un polinomio generador y guarda el resto. Detecta todos los errores de un bit, casi todos los de pocos bits, ráfagas de errores e intercambios de posición. El costo es algo de cálculo (en un procesador, una tabla de 256 entradas, una LUT) o un pequeño circuito con un registro de desplazamiento realimentado, como los del tema 14.
Un bootloader serio verifica el CRC de la aplicación antes de ejecutarla y se niega a arrancar si no coincide. Es la versión de hardware de ver que un archivo bajó completo.
08 · La ROM en código
Programar, tabular, muestrear y verificar
Los bloques modelan la regla de programación de una EPROM, implementan lógica con tablas (siete segmentos y un sumador), construyen tablas de seno de distinto tamaño y comparan un checksum con un CRC-8 ante errores inyectados.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
Paso 1: programar solo lleva de 1 a 0
class EPROM:
"""EPROM de 2^n palabras de 8 bits. Programar solo puede llevar bits de 1 a 0; borrar (UV) devuelve todo a 1."""
def __init__(self, n=4):
self.datos = [0xFF] * (2 ** n)
self.borrados = 0
def leer(self, a):
return self.datos[a]
def programar(self, a, valor):
anterior = self.datos[a]
self.datos[a] = anterior & valor # un 0 ya programado no vuelve a 1
return self.datos[a]
def borrar_uv(self):
self.datos = [0xFF] * len(self.datos)
self.borrados += 1
m = EPROM(4)
print("virgen: ", *(f"{m.leer(a):02X}" for a in range(8)), "...")
res = m.programar(3, 0xA5)
print(f"programo 0xA5 en 3: queda 0x{res:02X}")
res = m.programar(3, 0xFF)
print(f"intento 'programar' 0xFF encima: queda 0x{res:02X} (no se puede volver a 1 sin borrar)")
res = m.programar(3, 0x0F)
print(f"programo 0x0F encima de 0xA5: queda 0x{res:02X} (= 0xA5 AND 0x0F)")
m.borrar_uv()
print(f"tras borrar con UV: 0x{m.leer(3):02X}, ciclos de borrado: {m.borrados}")
res = m.programar(3, 0xA5)
print(f"reprogramo 0xA5: queda 0x{res:02X}")
La memoria virgen vale 0xFF. Programar 0xA5 deja 0xA5; querer «reescribir» 0xFF no cambia nada (queda 0xA5), y programar 0x0F encima da 0xA5 AND 0x0F = 0x05, no 0x0F. Solo el borrado con UV devuelve los bits a 1 y permite reprogramar. Es exactamente lo que hace el laboratorio de más abajo.
Paso 2: lógica en una ROM
# ROM de 16 x 7 que convierte un dígito hexadecimal en los segmentos a b c d e f g (cátodo común).
SEG = ["1111110", "0110000", "1101101", "1111001", "0110011", "1011011", "1011111", "1110000",
"1111111", "1111011", "1110111", "0011111", "1001110", "0111101", "1001111", "1000111"]
ROM7 = [int(b, 2) for b in SEG]
print("ROM de 7 segmentos (16 x 7 = 112 bits):")
print(" dirección a b c d e f g byte")
for a in (0, 1, 7, 8, 10, 15):
print(f" {a:X} {' '.join(SEG[a])} 0x{ROM7[a]:02X}")
# ROM de 256 x 5 que suma dos números de 4 bits: la dirección es (A << 4) | B
SUMA = [((addr >> 4) + (addr & 0xF)) for addr in range(256)]
errores = sum(1 for a in range(16) for b in range(16) if SUMA[(a << 4) | b] != a + b)
print()
print(f"ROM de suma: 256 palabras de 5 bits = {256 * 5} bits; errores contra la aritmética: {errores}")
print(f" ejemplo: 9 + 8 -> dirección 0x{(9 << 4) | 8:02X}, contenido {SUMA[(9 << 4) | 8]} = 0b{SUMA[(9 << 4) | 8]:05b}")
# crecimiento: una ROM para sumar dos números de n bits cada uno
print()
print("bits de ROM para un sumador de dos números de n bits (2n entradas, n+1 salidas):")
for n in (2, 4, 6, 8):
print(f" n = {n}: 2^{2 * n} = {2 ** (2 * n):6d} palabras x {n + 1} bits = {2 ** (2 * n) * (n + 1):7d} bits")
La tabla de siete segmentos es una ROM de 112 bits: el 0 es 0x7E (todo menos g), el 8 es 0x7F (todos), la A es 0x77. La ROM de suma de 256 palabras de 5 bits reproduce las 256 sumas sin ningún error (9 + 8 = 17, 0b10001, en la dirección 0x98). Pero crece rápido: de 1280 bits para 4 bits a casi 590 000 para 8 bits. Por eso una ROM sirve para funciones de pocas entradas.
Paso 3: una tabla de seno
from math import sin, pi
def tabla_seno(n_bits_dir, ancho=8):
N = 2 ** n_bits_dir
amplitud = 2 ** ancho - 1
return [round((sin(2 * pi * i / N) + 1) / 2 * amplitud) for i in range(N)]
def error_maximo(rom, n_bits_dir, ancho=8):
N = 2 ** n_bits_dir
amplitud = 2 ** ancho - 1
peor = 0
for k in range(4096): # recorremos la señal con más resolución que la tabla
x = k / 4096
ideal = (sin(2 * pi * x) + 1) / 2 * amplitud
leido = rom[int(x * N) % N] # sin interpolar: tomamos el valor de la entrada anterior
peor = max(peor, abs(ideal - leido))
return peor
rom = tabla_seno(6)
print("tabla de seno de 64 puntos, 8 bits (primeros valores):", *rom[:10])
print("valores clave: dir 0 ->", rom[0], "| dir 16 (90°) ->", rom[16], "| dir 32 (180°) ->", rom[32], "| dir 48 (270°) ->", rom[48])
print()
print("líneas de dirección | puntos | bits de ROM (8 bits por punto) | error máximo sin interpolar (LSB de 8 bits)")
for n in (4, 6, 8, 10):
r = tabla_seno(n)
print(f" {n:2d} | {2 ** n:5d} | {2 ** n * 8:6d} | {error_maximo(r, n):6.1f}")
# generador: un contador de n bits recorre la ROM y un DAC convierte
f_reloj = 64_000 # Hz
n = 6
print()
print(f"con un contador de {n} bits y reloj de {f_reloj} Hz: frecuencia de la señal = {f_reloj / 2 ** n:.0f} Hz")
La tabla de 64 puntos va de 128 (el centro) a 255 en el máximo (dirección 16, 90°), vuelve a 128 en la 32 y llega al mínimo 0 en la 48. Duplicar los puntos reduce el error de cuantización por escalón: 49 niveles con 16 puntos, 13 con 64, 3 con 256 y 1 con 1024. Cuatro veces más puntos cuestan cuatro veces más ROM por cada paso. Con un contador de 6 bits y 64 kHz, la señal sale a 1 kHz.
Paso 4: checksum y CRC
import random
def suma_comprobacion(imagen):
"""Complemento a 2 de la suma módulo 256: sumar todo el contenido con este byte da 0."""
return (-sum(imagen)) & 0xFF
def crc8(imagen, poly=0x07):
crc = 0
for byte in imagen:
crc ^= byte
for _ in range(8):
crc = ((crc << 1) ^ poly) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF
return crc
rnd = random.Random(28)
imagen = [rnd.randint(0, 255) for _ in range(256)]
chk, crc = suma_comprobacion(imagen), crc8(imagen)
print(f"imagen de 256 bytes: checksum = 0x{chk:02X}, CRC-8 = 0x{crc:02X}")
print("verificación (suma total con el checksum):", (sum(imagen) + chk) & 0xFF, "-> 0 significa 'sin cambios'")
# inyectamos errores y vemos qué detecta cada método
n_pruebas, detecta_chk, detecta_crc = 2000, 0, 0
for _ in range(n_pruebas):
mala = imagen[:]
pos = rnd.randrange(256)
mala[pos] ^= 1 << rnd.randrange(8) # un solo bit cambiado
detecta_chk += suma_comprobacion(mala) != chk
detecta_crc += crc8(mala) != crc
print(f"errores de 1 bit: checksum detecta {detecta_chk}/{n_pruebas}, CRC-8 detecta {detecta_crc}/{n_pruebas}")
# un error que el checksum NO ve: dos bytes que se compensan
mala = imagen[:]
mala[10] = (mala[10] + 5) & 0xFF
mala[200] = (mala[200] - 5) & 0xFF
print("dos bytes que se compensan (+5 y -5): checksum igual?", suma_comprobacion(mala) == chk, "| CRC-8 igual?", crc8(mala) == crc)
# intercambio de dos bytes
mala = imagen[:]
mala[3], mala[4] = mala[4], mala[3]
print("dos bytes intercambiados: checksum igual?", suma_comprobacion(mala) == chk, "| CRC-8 igual?", crc8(mala) == crc)
Con un error de un solo bit, tanto el checksum como el CRC-8 detectan los 2000 casos probados. La diferencia aparece en errores más sutiles: si dos bytes cambian en sentidos opuestos (+5 y −5), el checksum queda igual y no ve el problema, y si dos bytes se intercambian de lugar, tampoco. El CRC-8 sí detecta ambos. Por eso los bootloaders usan CRC y no una simple suma.
Los cuatro bloques cubren el ciclo de vida de una ROM: grabarla bajo sus reglas, usarla como lógica o como tabla, dimensionarla y verificar que lo grabado sea lo que se quiso grabar.
09 · Exploración interactiva
El laboratorio: programar una ROM
El simulador es una ROM de 16 palabras de 8 bits en tres versiones: de máscara (con la tabla de siete segmentos grabada de fábrica), PROM virgen (se programa una vez) y EPROM virgen (se programa y se borra con UV). Elegís una dirección y un dato en hexadecimal, y «Programar palabra» aplica la regla del AND: solo se pueden bajar bits. Con «Programar tabla de 7 segmentos» se carga la tabla completa, y «Leer» muestra el dato como segmentos de un display.
ROM de máscara, PROM o EPROM de 16 × 8
EPROM virgen: todas las palabras en 0xFF.
| N.º | Operación | Dirección | Dato | Resultado |
|---|---|---|---|---|
| Sin operaciones. | ||||
Recorrido guiado
- Con la EPROM virgen, presioná «Leer» en la dirección 0: el dato es 0xFF y el display enciende todos los segmentos. Una memoria sin programar tiene todos los bits en 1.
- Presioná «Programar tabla de 7 segmentos»: las 16 palabras toman los códigos de 0 a F, y la verificación dice «coincide con la tabla». Recorré las direcciones con «Leer»: el display muestra el dígito de cada una.
- En la dirección 5 (0x5B, el dígito 5), programá 0xFF: el contenido no cambia (queda 0x5B, los ceros no vuelven a 1), pero el simulador marca que la verificación falla, porque se pedía 0xFF y quedó otra cosa.
- En la misma dirección, programá 0x30 (que sería el 1): queda 0x5B AND 0x30 = 0x10, no 0x30. Aparece «verificación falla» y la tabla ya no coincide.
- Presioná «Borrar con UV»: todo vuelve a 0xFF y el contador de borrados sube. Reprogramá la tabla: vuelve a coincidir.
- Cambiá a PROM: se reinicia virgen. Programá la tabla (ok) y luego intentá reprogramar una palabra con otro valor: falla. «Borrar con UV» responde «imposible»: los fusibles quemados son definitivos.
- Cambiá a ROM de máscara: ya trae la tabla grabada. «Programar palabra» y «Programar tabla» son rechazados, y la verificación coincide desde el principio.
- Con cualquiera de las tres, recorré las 16 direcciones con «Leer» y observá que el display forma 0 1 2 3 4 5 6 7 8 9 A b C d E F: la ROM es un decodificador de siete segmentos.
Desafío: programar un dato con dos intentos
Querés dejar 0xC3 en una dirección de una EPROM virgen, pero primero programás 0xF0 por error. Después de eso, ¿se puede llegar a 0xC3? 0xF0 AND 0xC3 = 0xC0, que no es 0xC3: los bits 1 y 0 de 0xC3 quedaron en 0 y no pueden subir. La única salida es borrar toda la memoria con UV y volver a programar. Es la razón de que un programador de EPROM siempre haga primero el chequeo de borrado, y de que un error en una palabra obligue a reprogramar el chip entero.
10 · Comprensión
Confusiones frecuentes
«ROM significa que nunca se puede cambiar»
Solo la ROM de máscara. La PROM se programa una vez, la EPROM se borra con UV y reprograma, y la EEPROM y la Flash se reescriben eléctricamente. «Solo lectura» describe el uso en funcionamiento normal.
«Se puede reprogramar una palabra de EPROM sin borrar»
Programar solo baja bits (de 1 a 0). Para subirlos hay que borrar todo el chip con luz ultravioleta.
«Una EPROM nueva viene en cero»
Viene en 0xFF: todos los bits en 1. El borrado también deja todo en 1.
«La ROM solo sirve para guardar datos»
También implementa lógica combinacional: las direcciones son entradas y los datos, salidas. Un decodificador de siete segmentos o un conversor de código son ROM.
«Una ROM siempre es mejor que las compuertas porque no hay que simplificar»
El tamaño de la ROM se duplica por cada entrada que se agrega. Para funciones regulares y de muchas entradas, la lógica dedicada ocupa mucho menos.
«El firmware en ROM no puede tener errores de memoria»
Puede tenerlos: un bit puede perderse con los años o la programación puede fallar. Por eso se agrega un checksum o un CRC.
«El checksum detecta cualquier cambio»
No ve dos errores que se compensen ni bytes intercambiados. El CRC es mucho más robusto, por un costo de cálculo pequeño.
«Las ventanitas de las EPROM son decorativas»
Son el camino de la luz ultravioleta que las borra. Por eso se tapan con una etiqueta una vez programadas: la luz solar o la de algunas lámparas fluorescentes puede borrarlas de a poco.
11 · Práctica guiada
Predecí antes de simular
1. Tamaño de una tabla
Una ROM convierte un número BCD de 8 bits (dos dígitos) en binario de 7 bits. ¿Cuántas líneas de dirección, de datos, palabras y bits tiene?
Ver solución
8 líneas de dirección (256 palabras), 7 de datos: 256 · 7 = 1792 bits. En realidad, solo 100 de las 256 direcciones son BCD válidas (00 a 99); las otras 156 pueden quedar sin usar o marcar error.
2. El AND de la programación
En una EPROM, una palabra vale 0xA5. Se programa con 0xF0 y después con 0x3C. ¿Qué valor queda?
Ver solución
0xA5 AND 0xF0 = 0xA0. Luego 0xA0 AND 0x3C = 0x20. Ninguno de los dos datos queda en la memoria: cada programación solo baja bits.
3. Un comparador en ROM
Un comparador de dos números de 4 bits tiene tres salidas (A > B, A = B, A < B). ¿Qué tamaño tiene su ROM? ¿Por qué un 74HC85 es mejor?
Ver solución
8 entradas → 256 palabras de 3 bits = 768 bits. El 74HC85 hace lo mismo con pocas compuertas y se cascadea fácilmente para más bits; con una ROM, cada bit de más duplica la tabla.
4. Un generador de función
Una ROM con 256 muestras de seno es recorrida por un contador de 8 bits con reloj de 25,6 kHz. ¿Cuál es la frecuencia de la onda? ¿Y si se recorre una muestra de cada dos?
Ver solución
25 600 / 256 = 100 Hz. Saltando de a dos muestras, el ciclo completo tiene 128 pasos: 25 600 / 128 = 200 Hz (el doble, con la mitad de resolución).
5. Checksum a mano
Un firmware tiene los bytes 0x10, 0x20 y 0x30. ¿Cuál es el byte de checksum que hace que la suma total sea 0 módulo 256?
Ver solución
La suma es 0x60. El complemento a dos módulo 256 es 0x100 − 0x60 = 0xA0. Comprobación: 0x60 + 0xA0 = 0x100 → 0x00 módulo 256.
6. Diagnóstico
Se reprogramó una EPROM que ya tenía una versión anterior del firmware, sin borrarla antes. El programador dio error de verificación en varias palabras. ¿Por qué y cómo se arregla?
Ver solución razonada
Cada palabra quedó con (contenido viejo) AND (contenido nuevo): los bits que ya estaban en 0 no pudieron volver a 1, aunque la versión nueva los necesitara. Se arregla con un borrado completo con UV, un chequeo de borrado (todo 0xFF) y recién entonces programar y verificar de nuevo.
12 · Síntesis
Ideas para recordar
- Una ROM es una memoria no volátil que en funcionamiento normal solo se lee (no tiene WE̅). Se organiza como cualquier memoria: n líneas de dirección y m de datos.
- Por dentro es un decodificador (todos los minitérminos) y una matriz de conexiones: una tabla de verdad. Con ella se implementa cualquier función combinacional de n entradas y m salidas.
- ROM de máscara: contenido fijado en fábrica. PROM: se programa una vez quemando fusibles. EPROM: celda de compuerta flotante, se programa con tensión alta y se borra con luz ultravioleta.
- Programar solo lleva bits de 1 a 0 (resultado = anterior AND dato); borrar devuelve todo a 1 (0xFF). Antes de programar se hace un chequeo de borrado y después una verificación.
- Una tabla de consulta reemplaza un cálculo por una lectura: siete segmentos, senoides, conversiones de código, linealización de sensores; en software, un arreglo constante en Flash.
- El tamaño de una ROM crece 2n con las entradas: conviene con pocas entradas (hasta unas 10 a 12) y función irregular. Las LUT de una FPGA son pequeñas ROM.
- El contenido se verifica con un checksum (barato, débil) o un CRC (mucho más robusto); un bootloader serio comprueba el CRC antes de arrancar.
- El procesador arranca ejecutando lo que hay en la ROM en su vector de reset; sin ese contenido, no arranca.
¿Listo para el tema 29?
Antes de avanzar, explicá de memoria qué le pasa a una palabra de EPROM cuando se la programa dos veces, cómo se implementa una función lógica con una ROM y por qué una ROM de 16 entradas no es práctica para un sumador. Si podés justificar las tres respuestas, ya podés pasar a las memorias que se reescriben eléctricamente.
En el próximo tema veremos la EEPROM: datos que permanecen sin energía y se reescriben byte a byte sin luz ultravioleta, su retención y sus límites de ciclos de escritura.