01 · Punto de partida
Tres montajes sobre un mismo bus
El sistema del taller es el esqueleto de cualquier computadora sencilla: un procesador de 8 bits con 16 líneas de dirección (A0 a A15, 64 KiB), 8 de datos (D0 a D7) y tres señales de control activas en bajo: MREQ̅ (hay un acceso a memoria), RD̅ (el procesador lee) y WR̅ (el procesador escribe). Al bus se conectan tres bloques de 8 KiB cada uno:
| Bloque | Rango | A15 A14 A13 | Chip | Qué guarda |
|---|---|---|---|---|
| ROM | 0x0000 – 0x1FFF | 000 | EPROM de 8K × 8 | Programa de arranque y constantes |
| RAM | 0x2000 – 0x3FFF | 001 | SRAM de 8K × 8 | Variables y pila |
| E/S | 0x4000 – 0x5FFF | 010 | Latch de 8 bits (74HC574) | Puerto de salida a LED |
| (libre) | 0x6000 – 0xFFFF | 011 a 111 | — | Reservado para ampliar |
Cada montaje agrega un paso: primero el decodificador solo, después las memorias y por último el puerto, con las trampas de la decodificación parcial.
No hace falta tener los materiales: el código reproduce el decodificador y el mapa, y el laboratorio permite probar los tres esquemas con accesos de lectura y escritura.
02 · Preparación
Materiales y herramientas
| Cant. | Componente | Para qué sirve |
|---|---|---|
| 1 | 74HC138 (decodificador de 3 a 8) | Genera los CS̅ a partir de A15, A14 y A13 |
| 1 | EPROM o EEPROM de 8K × 8 (27C64 o 28C64) | La ROM del mapa |
| 1 | SRAM de 8K × 8 (6264) | La RAM del mapa |
| 1 | 74HC574 (latch octal) | Puerto de salida de 8 bits |
| 1 + 1 | 74HC32 (OR) y 74HC245 (transceptor de bus) | Pulso de escritura del puerto y manejo del bus de datos |
| 2 + 1 | DIP de 8 interruptores y DIP de 8 interruptores adicionales | Dirección (16 bits) y dato (8 bits) del «procesador manual» |
| 2 | Pulsadores con antirrebote (tema 7) | RD̅ y WR̅ |
| 1 | Interruptor | MREQ̅ |
| 16 | LED con resistencia de 1 kΩ | Bus de datos (8), salidas Y̅ del decodificador y puerto (8) |
| 8 + varios | Resistencias de 10 kΩ | Pull-ups del bus de datos y de las líneas de control |
| 6 | Condensador de 100 nF | Desacople, uno por chip |
| 1 | Protoboard grande, cables y fuente regulada de 5 V | Montaje y alimentación |
El «procesador manual» son los interruptores de dirección, los de dato y los pulsadores de RD̅ y WR̅: representan lo que haría una CPU. Para el bus de datos, la EPROM y la SRAM manejan el bus durante las lecturas; durante las escrituras lo maneja el 74HC245, que pasa los interruptores de dato al bus solo mientras WR̅ está activo. Dibujá el esquema en papel con los nombres de las señales antes de cablear: la mayor parte de los errores de este taller son cables cruzados entre buses.
Antes de dar tensión
- 100 nF por chip; ninguna entrada al aire (las líneas de control con pull-up a +5 V: MREQ̅, RD̅ y WR̅ inactivas).
- Verificá en la hoja de datos de cada memoria los pines CE̅, OE̅, WE̅ y los de programación (VPP y PGM de la EPROM, CS2 de la SRAM): los números de pin los fija el chip.
- La ROM debe estar programada con un contenido conocido (por ejemplo, el byte (7·dirección + 3) módulo 256) antes de empezar a probar lecturas.
03 · El mapa y el decodificador
El mapa y el decodificador 74HC138
Un bloque de 2k bytes necesita k líneas de dirección propias (A0 a A(k−1)), que van directamente al chip. Las líneas de dirección restantes, las altas, identifican el bloque. Para chips de 8 KiB (k = 13), las propias son A0 a A12 y las altas A13, A14 y A15: tres bits, ocho bloques de 8 KiB. Tres bits entran naturalmente a un decodificador de 3 a 8, el 74HC138.
| Pin | Nombre | Función | Pin | Nombre | Función |
|---|---|---|---|---|---|
| 1 | A | Entrada de dirección, bit 0 (←A13) | 16 | Vcc | +5 V |
| 2 | B | Bit 1 (←A14) | 15 | Y0̅ | Salida 0 (activa en bajo) |
| 3 | C | Bit 2 (←A15) | 14 | Y1̅ | Salida 1 |
| 4 | G2A̅ | Habilitación (activa en bajo) (←MREQ̅) | 13 | Y2̅ | Salida 2 |
| 5 | G2B̅ | Habilitación (activa en bajo) (←masa) | 12 | Y3̅ | Salida 3 |
| 6 | G1 | Habilitación (activa en alto) (←+5 V) | 11 | Y4̅ | Salida 4 |
| 7 | Y7̅ | Salida 7 | 10 | Y5̅ | Salida 5 |
| 8 | GND | Masa | 9 | Y6̅ | Salida 6 |
Con el decodificador habilitado (G1 = 1, G2A̅ = G2B̅ = 0), la salida Yn baja a 0 cuando C B A = n; las demás quedan en 1. Si no está habilitado, todas están en 1. Como las salidas son activas en bajo, se conectan directamente a las entradas CE̅ (chip enable) de las memorias, que también son activas en bajo.
| C B A | Salida activa | Se conecta a | Rango |
|---|---|---|---|
| 0 0 0 | Y0̅ | ROM · CE̅ | 0x0000 – 0x1FFF |
| 0 0 1 | Y1̅ | RAM · CE̅ | 0x2000 – 0x3FFF |
| 0 1 0 | Y2̅ | Puerto de salida (con WR̅) | 0x4000 – 0x5FFF |
| 0 1 1 a 1 1 1 | Y3̅ a Y7̅ | Sin conectar (libres) | 0x6000 – 0xFFFF |
Quién lee, quién escribe
El decodificador solo dice qué chip responde. Qué hace se decide con las señales de control, que se conectan igual a todos los chips (tema 26):
- RD̅ → OE̅ de la ROM y de la RAM: el chip seleccionado maneja el bus de datos solo durante una lectura.
- WR̅ → WE̅ de la RAM: el chip seleccionado guarda el dato solo durante una escritura. La ROM no tiene WE̅: una escritura a su rango se pierde.
- MREQ̅ → G2A̅ del decodificador: sin un acceso a memoria válido, ningún CE̅ baja. Así se evitan selecciones falsas mientras la dirección todavía está cambiando.
04 · Montaje 1
El decodificador con LED
Empezamos sin memorias: solo el 74HC138, los interruptores de dirección y ocho LED en las salidas. Es la forma más rápida de comprobar el mapa.
| Pin de U1 | Se conecta a |
|---|---|
| 16 / 8 | +5 V / masa, con 100 nF entre ambos |
| 1 (A) | Interruptor de A13 |
| 2 (B) | Interruptor de A14 |
| 3 (C) | Interruptor de A15 |
| 4 (G2A̅) | Interruptor de MREQ̅ (10 kΩ a +5 V; cerrado a masa = activo) |
| 5 (G2B̅) | Masa |
| 6 (G1) | +5 V |
| 15, 14, 13, 12, 11, 10, 9, 7 (Y0̅ … Y7̅) | Cada una a un LED con 1 kΩ hacia +5 V (el LED se enciende cuando la salida está en bajo) |
Pruebas
| A15 A14 A13 | LED encendido | Rango que representa | Qué se comprueba |
|---|---|---|---|
| 0 0 0 | Y0̅ | 0x0000 – 0x1FFF | Selección de la ROM |
| 0 0 1 | Y1̅ | 0x2000 – 0x3FFF | Selección de la RAM |
| 0 1 0 | Y2̅ | 0x4000 – 0x5FFF | Selección del puerto |
| 0 1 1, 1 0 0, 1 0 1, 1 1 0, 1 1 1 | Y3̅ a Y7̅ | 0x6000 – 0xFFFF | Zona libre: ningún chip conectado |
| cualquiera, con MREQ̅ = 1 | Ninguno | — | Sin acceso a memoria: todas las salidas en alto |
Si algún LED no se enciende en su turno, o dos lo hacen a la vez, hay un cable de dirección cruzado (el bit más significativo es C = A15, no A).
05 · Montaje 2
ROM y RAM en el bus
Ahora se conectan los chips. Todas las líneas de dirección bajas y de datos van en paralelo a los dos; lo que cambia es el CE̅.
| Señal | ROM (EPROM 8K × 8) | RAM (SRAM 8K × 8) |
|---|---|---|
| A0 a A12 | A0 a A12 | A0 a A12 |
| D0 a D7 | D0 a D7 (salidas) | D0 a D7 (E/S), todas al mismo bus |
| Selección | CE̅ ← Y0̅ del 74HC138 | CE̅ (CS1̅) ← Y1̅; CS2 a +5 V |
| Salida | OE̅ ← RD̅ | OE̅ ← RD̅ |
| Escritura | No existe (PGM y VPP a +5 V para lectura) | WE̅ ← WR̅ |
| Alimentación | +5 V / masa, con 100 nF | +5 V / masa, con 100 nF |
Para el bus de datos hay dos accesorios: ocho resistencias de 10 kΩ a +5 V (pull-ups) para que una lectura a una zona sin chip dé 0xFF en lugar de ruido, y los ocho LED conectados al bus para ver qué viaja por él. Los interruptores de dato entran al bus a través del 74HC245, con su dirección fija hacia el bus y su habilitación (OE̅) conectada a WR̅.
Pruebas
| Paso | Acción | Bus de datos | Qué se comprueba |
|---|---|---|---|
| 1 | Dirección 0x0003, pulsar RD̅ | Contenido de la ROM (0x18 con la fórmula 7a + 3) | Lectura de la ROM por CE̅ = Y0̅ y OE̅ = RD̅ |
| 2 | Dirección 0x0003, dato A5, pulsar WR̅ | Sin cambio al soltar | La ROM ignora las escrituras (no hay WE̅) |
| 3 | Dirección 0x2000, dato A5, pulsar WR̅ | — | La RAM guarda el dato (CE̅ = Y1̅, WE̅ = WR̅) |
| 4 | Dirección 0x2000, pulsar RD̅ | 0xA5 | Lectura de la RAM: el dato escrito |
| 5 | Dirección 0x2001, RD̅ | Valor indefinido (RAM sin inicializar) | Una RAM recién encendida no vale cero |
| 6 | Dirección 0x7000, RD̅ | 0xFF | Zona libre: solo los pull-ups manejan el bus |
| 7 | Cualquier dirección, MREQ̅ = 1, RD̅ | 0xFF | Sin MREQ̅ no responde nadie |
06 · Montaje 3
Puerto de salida, espejos y errores
El tercer bloque no es una memoria sino un puerto de salida: un latch de 8 bits (74HC574) cuyas salidas encienden ocho LED. Para la CPU es una posición de memoria más: escribir en 0x4000 cambia los LED.
| Conexión | Función |
|---|---|
| Y2̅ (del 74HC138) y WR̅ → U4 (OR) → CP de U3 (pin 11) | CP baja solo cuando el puerto está seleccionado Y hay escritura, y sube cuando termina WR̅: ese flanco captura el dato |
| D0 a D7 del bus → entradas D de U3 (pines 2 a 9) | El dato que la CPU tiene en el bus |
| U3 OE̅ (pin 1) a masa | Salidas siempre habilitadas |
| Salidas Q (pines 12 a 19) → LED con 1 kΩ | El valor del puerto |
| 100 nF y entradas libres a nivel definido | Alimentación y sin entradas al aire |
La captura en el flanco de subida de WR̅ importa: el dato y la dirección deben estar estables un poco antes y después de ese flanco (setup y hold, tema 11), y es justamente cuando los entrega la CPU en una escritura.
Prueba 1: el puerto
- Escribir 0x3C en 0x4000: los LED muestran 00111100 al soltar WR̅.
- Leer 0x4000: este puerto no maneja el bus, así que se lee 0xFF (zona libre). Un puerto de entrada llevaría un 74HC245 manejado por Y̅ y RD̅.
Prueba 2: decodificación parcial (espejos)
Desconectá A15 del decodificador y llevá C a masa. Ahora solo A14 y A13 deciden, y A15 «no importa»: cada bloque aparece dos veces en el mapa.
| Dirección | Responde | Comentario |
|---|---|---|
| 0x0000 y 0x8000 | ROM | La misma ROM en dos lugares |
| 0x2000 y 0xA000 | RAM | Escribir en 0xA000 cambia lo que se lee en 0x2000 |
| 0x4000 y 0xC000 | Puerto | Dos direcciones para el mismo puerto |
| 0x6000 y 0xE000 | Nadie | El espacio libre se reduce a la mitad |
Los espejos no son un error por sí mismos: son decodificación parcial, una forma de ahorrar una línea de decodificación cuando no hace falta el espacio completo. Pero hay que saberlo: el programa no debe usar el mismo dato por dos direcciones distintas sin querer, y el espacio libre se reduce.
Prueba 3: superposición (error)
Ahora llevá Y0̅ y Y1̅ al CE̅ de la RAM, de modo que la RAM responda también en 0x0000 – 0x1FFF. En ese rango, los dos chips (ROM y RAM) reciben CE̅ = 0 y OE̅ = 0 en una lectura: ambos manejan el bus de datos a la vez.
- Los LED del bus muestran valores a medias, ni los de la ROM ni los de la RAM.
- Los chips y la fuente pueden recibir corrientes anormales; si hay calentamiento, desconectá de inmediato.
- En una escritura, la RAM guarda el dato y la ROM lo ignora (sin WE̅): el error pasa desapercibido hasta que se lee.
07 · Cuando no funciona
Fallas, tiempos y método
| Síntoma | Causa probable | Cómo comprobarlo |
|---|---|---|
| Siempre se lee 0xFF de la ROM | ROM vacía, CE̅ u OE̅ sin activar, o PGM/VPP mal conectados | Medir CE̅ y OE̅ durante RD̅ (deben bajar); leer la ROM en un programador |
| La RAM «no guarda»: lee 0xFF o lo que había | WE̅ sin conectar, CS2 de la SRAM en bajo, o dato no presente en el bus durante la escritura | Medir WE̅ y CS2; mirar el bus de datos durante WR̅ |
| Se lee basura en una zona libre | Falta de pull-ups: el bus flota | Agregar 10 kΩ a +5 V en D0–D7 |
| Dos LED de Y̅ encendidos a la vez | Entradas A, B, C cruzadas o con un cable flotante | Probar las 8 combinaciones de la tabla del montaje 1 |
| Datos corruptos en la RAM al cambiar de dirección | WE̅ activo mientras la dirección todavía cambia, o glitches en CE̅ | WR̅ solo como pulso breve; la dirección debe estar estable antes de que baje WR̅ |
| Un chip se calienta | Contención: dos chips manejan el bus (CE̅ de ambos activos) | Revisar el mapa: nunca dos CE̅ a la vez; desconectar y medir |
| La ROM «responde» también en 0x8000 | Decodificación parcial: A15 ignorada | Comprobar que C = A15 llega al pin 3 |
| El puerto cambia al leer | La CP se arma con RD̅ en lugar de WR̅ | Medir CP durante RD̅: no debe moverse |
El retardo del decodificador
El 74HC138 tarda unos 15 a 25 ns en cambiar sus salidas tras cambiar la dirección. Para el chip de memoria, el CE̅ llega más tarde que la dirección, y el tiempo de acceso por CE̅ (tACS, tema 26) cuenta desde que CE̅ baja. Con una ROM de 150 ns y un decodificador de 20 ns, el dato está disponible 170 ns después de que la dirección es válida. El procesador debe darle al menos ese tiempo (con estados de espera si hace falta). Una memoria lenta combinada con un decodificador lento puede ser el origen de lecturas intermitentes cuando se sube la frecuencia del procesador.
Armar por etapas
- El decodificador primero, con LED, como en el montaje 1.
- Una memoria por vez: la ROM sola, leyendo; después la RAM, escribiendo y leyendo.
- Probar los límites de cada bloque (primera y última dirección) y una dirección fuera de todos.
- Revisar que nunca haya dos CE̅ activos recorriendo las ocho combinaciones de las líneas altas.
- Cambiar una sola cosa a la vez cuando algo falla, y anotar el resultado de cada prueba.
08 · El taller en código
Mapear, decodificar, espejar y detectar contención
Los bloques verifican que un mapa tenga bloques alineados y sin superposiciones, simulan el 74HC138 con su habilitación, comparan los tres esquemas de decodificación y cuentan las contenciones de bus de un mapa con error sobre accesos al azar.
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: el mapa y sus controles
# Mapa de un sistema con A0..A15: ROM de 8 KiB, RAM de 8 KiB y un bloque de E/S de 8 KiB.
REGIONES = [("ROM", 0x0000, 0x2000), ("RAM", 0x2000, 0x2000), ("E/S", 0x4000, 0x2000)]
print("región inicio fin tamaño líneas internas bits altos que la seleccionan (A15 A14 A13)")
for nombre, base, tam in REGIONES:
fin = base + tam - 1
n = tam.bit_length() - 1 # 13 líneas internas
alto = base >> 13
ok = tam & (tam - 1) == 0 and base % tam == 0
print(f"{nombre:6s} 0x{base:04X} 0x{fin:04X} {tam // 1024} KiB A{n - 1}..A0 ({n}) {alto:03b} {'(alineada)' if ok else '(NO ALINEADA)'}")
# comprobamos superposiciones
sup = [(a[0], b[0]) for i, a in enumerate(REGIONES) for b in REGIONES[i + 1:] if a[1] < b[1] + b[2] and b[1] < a[1] + a[2]]
print("\nsuperposiciones:", sup or "ninguna")
libre = 0x10000 - sum(t for _, _, t in REGIONES)
print(f"espacio total 64 KiB, ocupado {sum(t for _, _, t in REGIONES) // 1024} KiB, libre {libre // 1024} KiB (0x6000 a 0xFFFF)")
def decodificar(a):
for nombre, base, tam in REGIONES:
if base <= a < base + tam:
return nombre, a - base
return None, None
for a in (0x0000, 0x1FFF, 0x2000, 0x2ABC, 0x4000, 0x5FFF, 0x6000, 0xFFFF):
chip, off = decodificar(a)
print(f" 0x{a:04X} -> {chip if chip else 'nadie responde':14s}" + (f" posición interna 0x{off:04X}" if chip else ""))
Las tres regiones son bloques de 8 KiB alineados en múltiplos de su tamaño: se distinguen solo por los bits A15 A14 A13 (000, 001 y 010), y tienen 13 líneas internas (A12 a A0). No hay superposiciones y quedan libres 40 KiB (de 0x6000 a 0xFFFF). Las direcciones de ejemplo caen en el chip y la posición interna esperados; 0x6000 y 0xFFFF no tienen chip. Probá mover la RAM a 0x2800: el verificador la marcará como no alineada.
Paso 2: el 74HC138 y el retardo
def hc138(a, b, c, g1, g2a_n, g2b_n):
"""74HC138: salidas Y0..Y7 activas en bajo. Entradas A (bit 0), B (bit 1), C (bit 2)."""
y = [1] * 8
if g1 == 1 and g2a_n == 0 and g2b_n == 0:
y[c * 4 + b * 2 + a] = 0
return y
print("A15 A14 A13 MREQ̅ | Y0̅ Y1̅ Y2̅ Y3̅ Y4̅ Y5̅ Y6̅ Y7̅ | quién responde")
for a15, a14, a13, mreq in ((0, 0, 0, 0), (0, 0, 1, 0), (0, 1, 0, 0), (0, 1, 1, 0), (1, 1, 1, 0), (0, 0, 0, 1)):
y = hc138(a13, a14, a15, 1, mreq, 0) # A = A13, B = A14, C = A15; G2A̅ = MREQ̅
activo = [f"Y{i}" for i, v in enumerate(y) if v == 0]
chips = {"Y0": "ROM", "Y1": "RAM", "Y2": "E/S"}
quien = chips.get(activo[0], "nadie (zona libre)") if activo else "nadie (MREQ̅ = 1: no es un acceso a memoria)"
print(f" {a15} {a14} {a13} {mreq} | " + " ".join(str(v) for v in y) + f" | {quien}")
# retardo del decodificador: suma al tiempo de acceso
t_dec = 20 # ns, típico de un 74HC138
t_aa_rom = 150
print(f"\nel decodificador añade ~{t_dec} ns: una ROM de {t_aa_rom} ns de acceso por CS̅ debe respetar tACS contando ese retardo.")
print(f"la CPU debe esperar al menos {t_aa_rom + t_dec} ns desde que la dirección es válida hasta recibir el dato (sin contar el bus).")
La salida activa sigue el valor de C B A: 000 baja Y0̅ (ROM), 001 baja Y1̅ (RAM), 010 baja Y2̅ (puerto); 011 y 111 bajan Y3̅ y Y7̅, que no están conectadas (zona libre). Con MREQ̅ = 1 todas las salidas quedan en 1 y no responde nadie. Contando 20 ns del decodificador, una ROM de 150 ns entrega el dato 170 ns después de que la dirección es válida: ese es el tiempo que debe darle la CPU.
Paso 3: decodificación completa, parcial y con error
# ¿Qué direcciones activan cada chip si se decodifican menos líneas de las necesarias?
def responde(a, esquema):
a15, a14, a13 = (a >> 15) & 1, (a >> 14) & 1, (a >> 13) & 1
if esquema == "completa": # A15 A14 A13
sel = (a15, a14, a13)
tabla = {(0, 0, 0): "ROM", (0, 0, 1): "RAM", (0, 1, 0): "E/S"}
return [tabla[sel]] if sel in tabla else []
if esquema == "parcial": # se ignora A15
sel = (a14, a13)
tabla = {(0, 0): "ROM", (0, 1): "RAM", (1, 0): "E/S"}
return [tabla[sel]] if sel in tabla else []
if esquema == "error": # RAM decodificada solo con A15=0 y A14=0: se superpone con la ROM
r = []
if (a15, a14, a13) == (0, 0, 0): r.append("ROM")
if (a15, a14) == (0, 0): r.append("RAM")
if (a15, a14, a13) == (0, 1, 0): r.append("E/S")
return r
for esquema in ("completa", "parcial", "error"):
cuenta = {"ROM": 0, "RAM": 0, "E/S": 0, "nadie": 0, "conflicto": 0}
for bloque in range(8):
r = responde(bloque * 0x2000, esquema)
if len(r) == 0: cuenta["nadie"] += 1
elif len(r) > 1: cuenta["conflicto"] += 1
else: cuenta[r[0]] += 1
detalle = " ".join(f"{k}: {v}" for k, v in cuenta.items() if v)
print(f"{esquema:9s} (8 bloques de 8 KiB): {detalle}")
print()
print("espejos de la ROM con decodificación parcial (se ignora A15):")
print(" ", ", ".join(f"0x{b * 0x2000:04X}" for b in range(8) if responde(b * 0x2000, "parcial") == ["ROM"]))
print("con decodificación completa:")
print(" ", ", ".join(f"0x{b * 0x2000:04X}" for b in range(8) if responde(b * 0x2000, "completa") == ["ROM"]))
print()
print("esquema con error: bloques donde responden dos chips a la vez:",
", ".join(f"0x{b * 0x2000:04X}" for b in range(8) if len(responde(b * 0x2000, "error")) > 1))
Con decodificación completa, cada chip aparece una vez y quedan cinco bloques libres. Ignorando A15, cada chip se repite: la ROM aparece en 0x0000 y 0x8000, y los bloques libres bajan a dos (hay espejos, no espacio nuevo). En el esquema con error, la RAM se superpone con la ROM en el bloque 0x0000: ahí responden los dos chips a la vez, que es la contención de bus.
Paso 4: contención sobre accesos al azar
import random
rnd = random.Random(31)
ROM = bytes((i * 7 + 3) & 0xFF for i in range(8192))
RAM = bytearray([0] * 8192)
def mapa_correcto(a):
if a < 0x2000: return ["ROM"]
if a < 0x4000: return ["RAM"]
if a < 0x6000: return ["E/S"]
return []
def mapa_con_error(a):
r = []
if a < 0x2000: r.append("ROM")
if a < 0x4000: r.append("RAM") # RAM decodificada con A15 y A14 solamente: pisa a la ROM
if 0x4000 <= a < 0x6000: r.append("E/S")
return r
def acceso_lectura(a, mapa):
chips = mapa(a)
if not chips:
return 0xFF, "bus flotante (pull-ups)"
valores = []
for c in chips:
valores.append(ROM[a % 0x2000] if c == "ROM" else RAM[a % 0x2000] if c == "RAM" else 0x55)
if len(set(valores)) > 1 or len(chips) > 1:
return None, "CONTENCIÓN: " + " y ".join(chips)
return valores[0], chips[0]
for nombre, mapa in (("mapa correcto", mapa_correcto), ("mapa con superposición", mapa_con_error)):
contenciones = flot = ok = 0
for _ in range(5000):
a = rnd.randrange(0x10000)
v, quien = acceso_lectura(a, mapa)
if v is None: contenciones += 1
elif quien.startswith("bus"): flot += 1
else: ok += 1
print(f"{nombre:24s}: 5000 lecturas al azar -> {ok} normales, {flot} a zona libre, {contenciones} con contención")
print()
for a in (0x0000, 0x1234, 0x7000):
v, q = acceso_lectura(a, mapa_correcto)
print(f"lectura 0x{a:04X}: " + (f"0x{v:02X} desde {q}" if v is not None else q))
print("una lectura a una zona libre devuelve 0xFF si hay resistencias de pull-up, o ruido si el bus queda al aire: conviene que el programa nunca lea de ahí.")
Con el mapa correcto, 5000 lecturas al azar no producen ninguna contención: 1813 llegan a un chip y 3187 a la zona libre (el 64 % del espacio). Con la RAM superpuesta, 621 lecturas (cerca de 1/8, el tamaño del bloque 0x0000–0x1FFF) tienen a dos chips manejando el bus. Las lecturas a zona libre devuelven 0xFF por los pull-ups. Un solo acceso a la zona superpuesta ya alcanza para dañar un diseño real o producir datos incorrectos.
Los cuatro bloques muestran la misma lección: un mapa de memoria es un contrato entre el diseñador del hardware y el del software, y comprobarlo antes de armar lo vuelve casi automático.
09 · Exploración interactiva
El laboratorio: explorador de mapas
El simulador implementa el sistema del taller (ROM, RAM y puerto de salida de 8 KiB) con tres esquemas de decodificación: completa, parcial (se ignora A15) y con error (RAM superpuesta con la ROM). Escribís una dirección en hexadecimal, elegís lectura o escritura, y «Ejecutar acceso» muestra qué chips responden, qué hay en el bus y cuántos conflictos se acumularon. La barra de arriba es el mapa de 64 KiB dividido en ocho bloques; «Escanear» lo recorre y cuenta los bloques de cada chip.
Mapa de memoria de 64 KiB
Decodificación completa. Elegí una dirección y presioná «Ejecutar acceso».
| N.º | Operación | Dirección | Responde | Resultado |
|---|---|---|---|---|
| Sin accesos. | ||||
Recorrido guiado
- Con decodificación completa, leé la dirección 0000: responde la ROM con el byte (7·0 + 3) = 0x03. Leé 0003: 0x18. Leé 1FFF: aún ROM, última posición del bloque.
- Pasá a 2000, elegí Escritura con el dato A5 y ejecutá; después cambiá a Lectura y ejecutá en 2000: el bus muestra 0xA5. La RAM guardó el dato. Leé 2001: valor indefinido (RAM sin inicializar, en el simulador siempre cero).
- Escribí 3C en 4000: el puerto de salida pasa a 0x3C (00111100). Es una posición de memoria cuyo efecto son ocho LED.
- Leé 6000 y 8000: zona libre. Se lee 0xFF por los pull-ups y el contador de «accesos a zona libre» sube. Presioná «Escanear»: ROM 32, RAM 32, E/S 32, libre 160 (de 256 posiciones; 8 KiB = 32 posiciones).
- Cambiá a decodificación parcial: ahora 8000 responde como la ROM y A000 como la RAM. Escribí 77 en A000 y leé 2000: el 0x77 aparece en el espejo. «Escanear» da 64 posiciones de cada chip y 64 libres.
- Cambiá a «con error» y leé 0000: responden ROM y RAM a la vez, el simulador indica «conflicto de bus» y suma al contador. Leé 2000: la RAM sola responde con normalidad. «Escanear» muestra 32 posiciones en conflicto.
- Con el esquema con error, escribí en 1000: la RAM guarda y la ROM ignora (no tiene WE̅); el simulador marca «¡a la vez!» y cuenta un conflicto. En un circuito real la escritura pasa sin queja: el error se ve recién al leer.
- Marcá MREQ̅ = 1 en cualquier esquema: ningún CS̅ baja y el acceso no llega a ningún chip, sin importar la dirección.
Desafío: agregar una segunda RAM sin tocar el decodificador
Con decodificación completa, los bloques 0x6000 – 0x7FFF (Y3̅) están libres. Conectando Y3̅ al CE̅ de una segunda SRAM de 8 KiB y compartiendo A0 – A12, D0 – D7, OE̅ y WE̅, se obtiene RAM adicional en 0x6000 – 0x7FFF sin agregar un solo chip decodificador. Con decodificación parcial (A15 ignorada) esa RAM aparecería también en 0xE000: el espejo ocupa un bloque más del mapa. Esa es la razón de que se prefiera decodificar completo cuando el espacio hace falta.
10 · Comprensión
Confusiones frecuentes
«Cada chip ocupa exactamente el rango que necesita, empezando donde quiera»
Un bloque de 2k bytes debe empezar en una dirección múltiplo de 2k (estar alineado) para que las líneas altas lo identifiquen con un decodificador simple. Un bloque de 8 KiB en 0x2800 no se puede seleccionar con un 74HC138.
«El decodificador conecta al chip con el bus»
Solo genera señales de selección. El bus de datos y de dirección están siempre conectados a todos los chips; la selección decide cuál habilita sus salidas o su captura.
«Con CE̅ activo, la memoria ya entrega el dato»
Hace falta también OE̅ (lectura). Y para escribir, WE̅. CE̅ solo elige el chip.
«Una escritura a la ROM da error»
No: la ROM no tiene WE̅ y simplemente la ignora. Un programa con un error de dirección puede «escribir» en la ROM sin ninguna señal de falla.
«Las zonas libres devuelven siempre 0»
Devuelven lo que haya en el bus: 0xFF con pull-ups, o un valor indefinido si el bus flota. El programa nunca debe depender de ese valor.
«Un espejo de memoria es un error de diseño»
Es una decodificación parcial, una elección que ahorra compuertas a cambio de espacio. Es un error solo si no se sabe que existe.
«Basta con que dos chips tengan el mismo dato para que no haya problema al superponerse»
Aunque coincidan los valores, la contención existe; los datos distintos o las pequeñas diferencias de nivel y tiempo la vuelven dañina.
«El decodificador no afecta el tiempo de acceso»
Añade su propio retardo (15 a 25 ns en un 74HC138) antes de que el CE̅ llegue a la memoria, y eso se suma al tiempo de acceso total.
11 · Práctica guiada
Predecí antes de armar
1. Bloques de 4 KiB
Se quiere repartir 64 KiB en bloques de 4 KiB. ¿Cuántas líneas internas tiene cada chip y cuántas líneas de dirección altas hay que decodificar? ¿Cuántos bloques hay?
Ver solución
4 KiB = 212: 12 líneas internas (A0 a A11). Quedan A12 a A15: 4 líneas altas y 24 = 16 bloques. Se necesita un decodificador de 4 a 16 (74HC154, o dos 74HC138 con A15 como habilitación).
2. A qué chip llega
Con el mapa del taller y decodificación completa, ¿qué responde en 0x2ABC, en 0x4FFF y en 0x6800? ¿En qué posición interna?
Ver solución
0x2ABC: RAM, posición interna 0x0ABC. 0x4FFF: puerto de salida, posición 0x0FFF (el puerto ocupa un bloque de 8 KiB pero usa una sola posición útil; el resto son espejos del mismo latch). 0x6800: nadie (zona libre).
3. Espejos
Se ignora A15 en la decodificación. ¿En qué rangos responde la RAM?
Ver solución
La RAM responde cuando A14 A13 = 01, con A15 = 0 o 1: 0x2000 – 0x3FFF y 0xA000 – 0xBFFF. Son la misma memoria física en dos lugares.
4. Superposición
El CE̅ de la RAM se conecta por error a Y0̅ además de Y1̅ (con una compuerta AND). ¿Qué ocurre al leer 0x1000?
Ver solución razonada
Y0̅ = 0 selecciona la ROM, y por la AND también la RAM (CE̅ de la RAM = Y0̅ AND Y1̅ = 0 cuando cualquiera es 0). En una lectura con RD̅ = 0, ambos tienen CE̅ y OE̅ activos: los dos manejan el bus de datos a la vez (contención). El dato leído es incierto y los chips y la fuente sufren.
5. Margen de tiempo
La CPU entrega la dirección y toma el dato 200 ns después. La ROM tiene tACS = 150 ns y el decodificador 20 ns. ¿Alcanza? ¿Cuánto margen queda?
Ver solución
El dato está listo a los 20 + 150 = 170 ns. Sobran 200 − 170 = 30 ns: alcanza, con margen (conviene que sea mayor para absorber tolerancias y la propagación en el bus).
6. Diagnóstico
El programa escribe 0xA5 en 0x2000 y al leer obtiene 0xFF, aunque la lectura de la ROM funciona perfecto. ¿Cuáles son las causas más probables y cómo las separás?
Ver solución razonada
La ROM funciona, así que el decodificador y el bus de datos hacia la CPU están bien. Falla algo propio de la RAM: (a) OE̅ de la RAM sin conectar (no maneja el bus en la lectura y los pull-ups dan 0xFF), (b) WE̅ sin conectar o CS2 en bajo (no guardó el dato, aunque lea bien por OE̅). Se separan midiendo: durante la escritura, WE̅ y CE̅ de la RAM deben bajar; durante la lectura, OE̅ y CE̅ deben bajar.
12 · Síntesis
Ideas para recordar
- Un chip de 2k bytes recibe las líneas A0 a A(k−1); las líneas altas restantes, decodificadas, producen su CS̅. Los bloques deben estar alineados en múltiplos de su tamaño.
- Un 74HC138 convierte A15, A14 y A13 en ocho CS̅ activos en bajo para ocho bloques de 8 KiB; MREQ̅ en una entrada de habilitación evita selecciones falsas.
- El decodificador solo selecciona el chip. RD̅ se conecta a OE̅ de todas las memorias y WR̅ a WE̅ de las que se escriben; la ROM no tiene WE̅ e ignora las escrituras.
- En cada acceso debe estar activo un solo CS̅. Dos chips seleccionados a la vez provocan contención del bus de datos.
- Decodificación parcial (se ignoran líneas altas) produce espejos: el mismo chip en varios rangos, a cambio de ahorrar lógica y perder espacio.
- Una zona sin chip devuelve el valor del bus (0xFF con pull-ups, indefinido si flota); un puerto de salida es una posición de memoria cuyo efecto son las salidas de un latch.
- El decodificador añade 15 a 25 ns al tiempo de acceso: la CPU debe esperar al menos tdecodificador + tACS.
- El mapa de memoria es un contrato entre hardware y software: se escribe, se verifica con código y se prueba por etapas.
¿Listo para el tema 32?
Antes de avanzar, explicá de memoria cómo decodificarías tres bloques de 8 KiB con un 74HC138, qué conecta cada chip a RD̅ y WR̅ y qué pasa si dos chips se seleccionan a la vez. Si podés justificar las tres respuestas, ya podés cerrar el bloque de memorias y pasar a la temporización.
En el próximo tema veremos los temporizadores y monoestables: cómo generar pulsos de duración definida con redes RC y con el circuito integrado 555, y qué lugar ocupan en un sistema digital.