← Índice del curso

Circuitos Secuenciales y Memoria · Tema 31

Taller: mapa de memoria y selección

Un procesador ve una sola línea de direcciones enorme y un solo bus de datos. Lo que hay del otro lado (una ROM con el programa, una RAM para las variables, un puerto que enciende luces) son chips distintos que tienen que repartirse ese espacio sin pisarse. El taller trata de eso: dibujar el mapa de memoria, armar con un 74HC138 el circuito que decide qué chip responde a cada dirección y conectar bien CS̅, OE̅ y WE̅ para que, en cada instante, hable un solo chip.

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:

El mapa de memoria del taller (decodificación completa con A15, A14 y A13).
BloqueRangoA15 A14 A13ChipQué guarda
ROM0x0000 – 0x1FFF000EPROM de 8K × 8Programa de arranque y constantes
RAM0x2000 – 0x3FFF001SRAM de 8K × 8Variables y pila
E/S0x4000 – 0x5FFF010Latch de 8 bits (74HC574)Puerto de salida a LED
(libre)0x6000 – 0xFFFF011 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

Lista de materiales.
Cant.ComponentePara qué sirve
174HC138 (decodificador de 3 a 8)Genera los CS̅ a partir de A15, A14 y A13
1EPROM o EEPROM de 8K × 8 (27C64 o 28C64)La ROM del mapa
1SRAM de 8K × 8 (6264)La RAM del mapa
174HC574 (latch octal)Puerto de salida de 8 bits
1 + 174HC32 (OR) y 74HC245 (transceptor de bus)Pulso de escritura del puerto y manejo del bus de datos
2 + 1DIP de 8 interruptores y DIP de 8 interruptores adicionalesDirección (16 bits) y dato (8 bits) del «procesador manual»
2Pulsadores con antirrebote (tema 7)RD̅ y WR̅
1InterruptorMREQ̅
16LED con resistencia de 1 kΩBus de datos (8), salidas Y̅ del decodificador y puerto (8)
8 + variosResistencias de 10 kΩPull-ups del bus de datos y de las líneas de control
6Condensador de 100 nFDesacople, uno por chip
1Protoboard grande, cables y fuente regulada de 5 VMontaje 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.

Pinout del 74HC138 (confirmalo en la hoja de datos de tu fabricante).
PinNombreFunciónPinNombreFunción
1AEntrada de dirección, bit 0 (←A13)16Vcc+5 V
2BBit 1 (←A14)15Y0̅Salida 0 (activa en bajo)
3CBit 2 (←A15)14Y1̅Salida 1
4G2A̅Habilitación (activa en bajo) (←MREQ̅)13Y2̅Salida 2
5G2B̅Habilitación (activa en bajo) (←masa)12Y3̅Salida 3
6G1Habilitación (activa en alto) (←+5 V)11Y4̅Salida 4
7Y7̅Salida 710Y5̅Salida 5
8GNDMasa9Y6̅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.

Conexiones de selección (C = A15, B = A14, A = A13; G2A̅ = MREQ̅).
C B ASalida activaSe conecta aRango
0 0 0Y0̅ROM · CE̅0x0000 – 0x1FFF
0 0 1Y1̅RAM · CE̅0x2000 – 0x3FFF
0 1 0Y2̅Puerto de salida (con WR̅)0x4000 – 0x5FFF
0 1 1 a 1 1 1Y3̅ 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.

Conexiones del montaje 1. U1 = 74HC138.
Pin de U1Se 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

Secuencia de pruebas del montaje 1 (MREQ̅ activo salvo indicación).
A15 A14 A13LED encendidoRango que representaQué se comprueba
0 0 0Y0̅0x0000 – 0x1FFFSelección de la ROM
0 0 1Y1̅0x2000 – 0x3FFFSelección de la RAM
0 1 0Y2̅0x4000 – 0x5FFFSelección del puerto
0 1 1, 1 0 0, 1 0 1, 1 1 0, 1 1 1Y3̅ a Y7̅0x6000 – 0xFFFFZona libre: ningún chip conectado
cualquiera, con MREQ̅ = 1Ninguno—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̅.

Conexiones del montaje 2 (los números de pin de las memorias, según su hoja de datos).
SeñalROM (EPROM 8K × 8)RAM (SRAM 8K × 8)
A0 a A12A0 a A12A0 a A12
D0 a D7D0 a D7 (salidas)D0 a D7 (E/S), todas al mismo bus
SelecciónCE̅ ← Y0̅ del 74HC138CE̅ (CS1̅) ← Y1̅; CS2 a +5 V
SalidaOE̅ ← RD̅OE̅ ← RD̅
EscrituraNo 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

Secuencia de pruebas del montaje 2.
PasoAcciónBus de datosQué se comprueba
1Direcció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̅
2Dirección 0x0003, dato A5, pulsar WR̅Sin cambio al soltarLa ROM ignora las escrituras (no hay WE̅)
3Dirección 0x2000, dato A5, pulsar WR̅—La RAM guarda el dato (CE̅ = Y1̅, WE̅ = WR̅)
4Dirección 0x2000, pulsar RD̅0xA5Lectura de la RAM: el dato escrito
5Dirección 0x2001, RD̅Valor indefinido (RAM sin inicializar)Una RAM recién encendida no vale cero
6Dirección 0x7000, RD̅0xFFZona libre: solo los pull-ups manejan el bus
7Cualquier dirección, MREQ̅ = 1, RD̅0xFFSin 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.

Conexiones del puerto. U3 = 74HC574; U4 = 74HC32 (OR).
ConexiónFunció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 masaSalidas siempre habilitadas
Salidas Q (pines 12 a 19) → LED con 1 kΩEl valor del puerto
100 nF y entradas libres a nivel definidoAlimentació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.

Efecto de ignorar A15 en la decodificación.
DirecciónRespondeComentario
0x0000 y 0x8000ROMLa misma ROM en dos lugares
0x2000 y 0xA000RAMEscribir en 0xA000 cambia lo que se lee en 0x2000
0x4000 y 0xC000PuertoDos direcciones para el mismo puerto
0x6000 y 0xE000NadieEl 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

De síntoma a causa en los montajes de este tema.
SíntomaCausa probableCómo comprobarlo
Siempre se lee 0xFF de la ROMROM vacía, CE̅ u OE̅ sin activar, o PGM/VPP mal conectadosMedir CE̅ y OE̅ durante RD̅ (deben bajar); leer la ROM en un programador
La RAM «no guarda»: lee 0xFF o lo que habíaWE̅ sin conectar, CS2 de la SRAM en bajo, o dato no presente en el bus durante la escrituraMedir WE̅ y CS2; mirar el bus de datos durante WR̅
Se lee basura en una zona libreFalta de pull-ups: el bus flotaAgregar 10 kΩ a +5 V en D0–D7
Dos LED de Y̅ encendidos a la vezEntradas A, B, C cruzadas o con un cable flotanteProbar las 8 combinaciones de la tabla del montaje 1
Datos corruptos en la RAM al cambiar de direcciónWE̅ 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 calientaContenció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 0x8000Decodificación parcial: A15 ignoradaComprobar que C = A15 llega al pin 3
El puerto cambia al leerLa 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

  1. El decodificador primero, con LED, como en el montaje 1.
  2. Una memoria por vez: la ROM sola, leyendo; después la RAM, escribiendo y leyendo.
  3. Probar los límites de cada bloque (primera y última dirección) y una dirección fuera de todos.
  4. Revisar que nunca haya dos CE̅ activos recorriendo las ocho combinaciones de las líneas altas.
  5. 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

Mapa de memoria simulado Una barra de ocho bloques de 8 KiB coloreados según el chip que responde en cada uno: ROM, RAM, E/S, libre o conflicto. Un marcador indica la dirección elegida. Debajo, tres lámparas muestran los CS̅ de ROM, RAM y E/S.
Dirección0x0000
A15 A14 A130 0 0
Chip(s) seleccionado(s)ROM
Posición interna (A12…A0)0x0000
Bus de datos—
Puerto de salida0x00 (00000000)
Accesos0
Conflictos de bus0
Accesos a zona libre0
Último escaneo—

Decodificación completa. Elegí una dirección y presioná «Ejecutar acceso».

Últimos 8 accesos desde el inicio de la simulación.
N.ºOperaciónDirecciónRespondeResultado
Sin accesos.

Recorrido guiado

  1. 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.
  2. 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).
  3. Escribí 3C en 4000: el puerto de salida pasa a 0x3C (00111100). Es una posición de memoria cuyo efecto son ocho LED.
  4. 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).
  5. 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.
  6. 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.
  7. 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.
  8. 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.