← Índice del curso

Circuitos Secuenciales y Memoria · Tema 26

Memorias: organización y buses

Hasta ahora guardamos un bit con un flip-flop y una palabra con un registro. Una memoria es la extensión natural: miles de palabras en un solo chip, cada una con una dirección, de las que se lee o se escribe una por vez. Para el programador es un arreglo de bytes; para el circuito es una tabla de registros con un decodificador y un selector. Este tema explica cómo se organiza, qué significan sus pines (direcciones, datos, control) y cómo se hacen un ciclo de lectura y uno de escritura.

01 · Punto de partida

De un registro a una memoria

Un registro (tema 13) guarda una palabra y la entrega siempre en sus salidas. Si se necesitaran 1024 palabras habría que repetirlo 1024 veces, y cada copia tendría su propio juego de entradas y salidas: miles de cables. Una memoria resuelve el problema con una idea simple: compartir los cables de datos entre todas las palabras y usar unas pocas líneas de dirección para elegir con cuál de ellas se habla en cada momento.

La analogía con la programación es directa: una memoria es un arreglo. La dirección es el índice, la palabra es el elemento, leer es x = mem[dir] y escribir es mem[dir] = x. La diferencia es que en el hardware el «índice» son cables, el «acceso» es un ciclo de reloj y varias señales de control deciden si lo que se hace es leer, escribir o nada.

Del registro a la memoria.
ElementoRegistro (tema 13)Memoria
Palabras guardadas12n
Cómo se elige la palabraNo hace falta: es una solaCon n líneas de dirección
Entradas de datosUna por bitUna por bit, compartidas por todas las palabras
Cuándo cargaHabilitación EN y flancoSeñal de escritura WE̅ sobre la palabra elegida
SalidaSiempre presenteSolo cuando se la pide (OE̅), en un bus compartido de tres estados

02 · Organización

Palabras, direcciones y capacidad

Una memoria se describe con dos números: cuántas palabras tiene y cuántos bits tiene cada palabra. Con n líneas de dirección se distinguen 2n palabras; con m líneas de datos se leen o escriben m bits por vez. Se escribe 2n × m (por ejemplo, «2K × 8» o «16K × 16»).

palabras = 2n   |   capacidad (bits) = 2n · m   |   capacidad (bytes) = 2n · m / 8
Organización de memorias conocidas (las líneas de dirección salen de n = log2 (palabras)).
MemoriaPalabrasBits por palabraLíneas ABytesRango
6116 (SRAM)204881120480x000 – 0x7FF
62256 (SRAM)32 76881532 7680x0000 – 0x7FFF
27C256 (EPROM)32 76881532 7680x0000 – 0x7FFF
28C256 (EEPROM)32 76881532 7680x0000 – 0x7FFF
16K × 16 (genérica)16 384161432 7680x0000 – 0x3FFF

Dos advertencias de notación que confunden a casi todos:

  • K, M y G en memorias son potencias de 2: 1 K = 1024, no 1000. Un bus de dirección de 16 bits direcciona 65 536 posiciones = 64 K.
  • Bits no es lo mismo que bytes: «256 Kb» son 256 · 1024 bits = 32 KB (kilobytes). Un chip 32K × 8 son 256 Kbit, y por eso se vende como «256K» aunque tenga 32 KB.

Si necesitás almacenar 1000 palabras, no existe un chip de 1000: hacen falta 10 líneas de dirección (1024 palabras) y quedan 24 sin usar. La cantidad de palabras siempre es una potencia de dos, y la dirección más alta es 2n − 1.

03 · Estructura interna

Cómo es por dentro

La forma más clara de imaginar una memoria es como un banco de registros con dos circuitos combinacionales a los lados: un decodificador que decide cuál de los registros se escribe, y un multiplexor que decide cuál se lee.

Memoria de cuatro palabras como banco de registros Dos líneas de dirección entran a un decodificador de 2 a 4 que genera la habilitación de escritura de cuatro registros. Los datos de entrada llegan a todos los registros. Un multiplexor de 4 a 1, controlado por las mismas líneas de dirección, elige la palabra que sale. Decodificador2 a 4 Palabra 0Palabra 1Palabra 2Palabra 3 MUX 4:1 A1A0 EN0EN1EN2EN3 dato leído dato a escribir: entra a todas las palabras A1 A0 eligen la palabra que sale

Para escribir en la palabra 2, el decodificador activa solo EN2 (y las demás ENk quedan inactivas), y el dato de entrada se carga en ese registro en el flanco. Para leer, el multiplexor lleva a la salida la palabra que indican A1 A0. Esto es exactamente lo que se estudió en los temas 13 y 14, repetido 2n veces con un par de circuitos combinacionales.

Por qué los chips son matrices

La estructura anterior se vuelve inviable para memorias grandes: un decodificador con 65 536 salidas para 64K palabras tendría 65 536 compuertas y 65 536 cables largos. Los chips reales organizan las celdas en una matriz de filas y columnas y dividen la dirección en dos mitades: una mitad elige la fila (decodificador de filas) y la otra elige la columna (multiplexor de columnas). Para 64K × 1 con 16 líneas de dirección:

Decodificar 64K posiciones con una sola etapa o con una matriz.
EsquemaSalidas de decodificación
Un solo decodificador de 16 a 65 53665 536 líneas
Matriz de 256 × 256 (8 bits de fila y 8 de columna)256 + 256 = 512 líneas

La reducción es enorme: 128 veces menos líneas. Es la razón por la que los chips son cuadrados, y por la que algunas memorias dinámicas (tema 27) reciben la dirección en dos mitades por los mismos pines, uno tras otro. Para el uso desde afuera, sin embargo, el modelo del banco de registros alcanza.

04 · Buses

Los tres buses

Un bus es un conjunto de líneas que viajan juntas y tienen un mismo propósito. Una memoria se conecta al resto del sistema con tres:

Los tres buses de una memoria.
BusLíneasSentidoPara qué sirve
DirecciónA0 … A(n−1)Hacia la memoria (la maneja el procesador)Elegir la palabra
DatosD0 … D(m−1)Bidireccional, de tres estadosEntregar el dato leído o recibir el dato a escribir
ControlCS̅, OE̅, WE̅ (y otras)Hacia la memoriaDecir si hay que leer, escribir o no hacer nada

Los pines de datos son bidireccionales porque las mismas líneas se usan para leer y para escribir; para eso necesitan salidas de tres estados (tema 13): durante una lectura las maneja la memoria, durante una escritura las maneja el procesador, y el resto del tiempo nadie. Las tres señales de control, activas en bajo, resuelven quién habla:

Funcionamiento de una SRAM asíncrona típica según CS̅, OE̅ y WE̅ (X = sin importancia).
CS̅OE̅WE̅ModoPines de datos
1XXDeshabilitada (reposo)Alta impedancia (Z)
001LecturaSalida: dato de la palabra elegida
0X0EscrituraEntrada: el dato se guarda
011Habilitada sin salidaAlta impedancia (Z)
  • CS̅ (chip select): elige el chip. Con CS̅ = 1 la memoria ignora todo y deja el bus libre; es lo que permite que haya varias memorias en el mismo bus.
  • OE̅ (output enable): pide que la memoria maneje el bus de datos. Sin OE̅ activo, la memoria no escribe en el bus aunque esté leyendo internamente.
  • WE̅ (write enable): ordena guardar el dato presente en el bus en la palabra elegida.

Cuando OE̅ y WE̅ están ambos activos a la vez, el comportamiento depende del chip (en muchos, WE̅ manda; en otros hay que evitarlo): se lo consulta en la hoja de datos, y el sistema se diseña para que no ocurra.

05 · Ciclos

Ciclos de lectura y de escritura

Lectura

  1. El procesador coloca la dirección en el bus de dirección.
  2. Activa CS̅ y OE̅ (con WE̅ inactivo).
  3. Pasado un tiempo, la memoria pone el dato en el bus de datos.
  4. El procesador lo captura (normalmente con un flanco de reloj) y retira las señales.
Ciclo de lectura de una SRAM asíncrona La dirección se vuelve válida, CS̅ y OE̅ bajan, y después del tiempo de acceso el dato aparece válido en el bus de datos, que antes estaba en alta impedancia. DirecciónCS̅OE̅DatosZ (alta impedancia)válido dirección y control válidosdato válido (tiempo de acceso)
Tiempos de una lectura (valores típicos de una SRAM de 70 ns).
ParámetroSignificadoValor típico
tAAAcceso por dirección: de dirección estable a dato válido70 ns
tACSAcceso por CS̅: de CS̅ bajo a dato válido70 ns
tOEDe OE̅ bajo a dato válido35 ns
tOHCuánto se mantiene el dato tras cambiar la dirección≥ 5 ns

El dato es válido cuando se cumplen todas las condiciones: el tiempo total es el máximo de (dirección + tAA), (CS̅ + tACS) y (OE̅ + tOE). Si la dirección llega 20 ns tarde, el dato llega 20 ns tarde.

Escritura

  1. El procesador coloca la dirección y el dato en los buses.
  2. Activa CS̅ y baja WE̅.
  3. Mantiene WE̅ bajo un ancho mínimo; el dato se guarda al subir WE̅ (en muchas memorias).
  4. Mantiene el dato y la dirección estables un instante más (hold) y retira las señales.
Tiempos de una escritura (valores típicos).
ParámetroSignificadoValor típico
tWPAncho mínimo del pulso de WE̅50 ns
tAWDirección estable antes de que suba WE̅60 ns
tDWDato estable antes de que suba WE̅ (setup)30 ns
tDHDato estable después de que sube WE̅ (hold)0 a 5 ns

Si algún tiempo no se cumple, la memoria guarda un dato incorrecto o en otra dirección: la misma lección de setup y hold del tema 11, ahora aplicada a un chip de memoria. Y un error clásico de escritura: cambiar la dirección mientras WE̅ está bajo. Puede escribir el dato en la dirección vieja, en la nueva o en las dos; la dirección debe estar estable durante todo el pulso.

06 · Ampliar

Ampliar una memoria

Cuando un chip no alcanza, se combinan varios. Hay dos formas de ampliar, que se pueden mezclar.

En ancho: más bits por palabra

Dos chips de 8K × 8 forman una memoria de 8K × 16. Los dos reciben las mismas líneas de dirección y los mismos CS̅, OE̅ y WE̅; cada uno aporta 8 de los 16 bits de datos. Es como poner dos arreglos paralelos.

En profundidad: más palabras

Dos chips de 8K × 8 forman una memoria de 16K × 8. Comparten las 13 líneas de dirección bajas (A0 a A12), los pines de datos y OE̅ / WE̅, pero cada uno tiene su propio CS̅. La línea de dirección que sobra, A13, elige el chip: con A13 = 0 responde el primero y con A13 = 1 el segundo (en general a través de un decodificador, tema 31).

Dos 8K × 8 como 16K × 8 (profundidad): A13 selecciona el chip.
ChipA13Rango de direccionesPosición interna
U000x0000 – 0x1FFFA12…A0
U110x2000 – 0x3FFFA12…A0

En un sistema con varias memorias y periféricos, el reparto de rangos de direcciones se llama mapa de memoria. Diseñarlo es decidir qué direcciones activan cada chip, sin superposición (dos chips respondiendo a la misma dirección producirían contención) y sin huecos inesperados. Se estudia con detalle en el taller del tema 31.

07 · Para quien programa

La memoria vista desde el programa

Todo lo que hace un programa con variables, arreglos y punteros se apoya en este modelo. Algunas correspondencias:

Conceptos de programación y su contraparte física.
En el programaEn la memoria
Arreglo de bytes uint8_t mem[65536]Una memoria de 64K × 8: 16 líneas de dirección, 8 de datos
Índice o punteroEl valor puesto en el bus de dirección
x = mem[i]Ciclo de lectura: CS̅ y OE̅ activos
mem[i] = xCiclo de escritura: CS̅ y WE̅ activos
Puntero nulo o fuera de rangoDirección sin chip (nadie maneja el bus) o con otro chip
Variable de 16 bits en memoria de 8 bitsDos posiciones consecutivas; el orden de los bytes se llama endianness
Alineación (dirección múltiplo de 2 o 4)Palabras de bus de 16 o 32 bits leídas en un solo ciclo

Dos ejemplos de la vida diaria del programador. Primero, el límite del espacio direccionable: un microcontrolador de 8 bits con 16 líneas de dirección ve a lo sumo 64 KiB; para más, usa bancos o direcciones más anchas. Segundo, las variables multibyte: un entero de 16 bits ocupa dos posiciones; si el byte menos significativo va en la dirección más baja se llama little-endian (usado por x86 y ARM en su configuración habitual) y, si va en la más alta, big-endian (usado en muchos protocolos de red). El hardware no lo decide: lo decide la convención con que se escribe y se lee.

08 · La memoria en código

Modelar, calcular, ampliar y medir

Los bloques implementan una memoria con sus señales de control y el conflicto de bus, calculan organizaciones y capacidades, arman una memoria ampliada en profundidad y en ancho y verifican tiempos de lectura y de escritura.

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: una memoria con CS̅, OE̅ y WE̅

class Memoria:
    """Memoria de 2^n palabras de m bits con las señales CS̅, OE̅ y WE̅ (activas en bajo)."""

    def __init__(self, n, m):
        self.n, self.m = n, m
        self.celdas = [0] * (2 ** n)

    def ciclo(self, direccion, cs_n, oe_n, we_n, dato_cpu=None):
        """Devuelve (modo, lo que la memoria pone en el bus). dato_cpu = valor que maneja el procesador, o None si no lo maneja."""
        if not 0 <= direccion < 2 ** self.n:
            raise ValueError("dirección fuera de rango")
        if cs_n == 1:
            return "deshabilitada", "Z"
        if we_n == 0:                                     # escritura (WE̅ tiene prioridad sobre OE̅ en esta memoria)
            if dato_cpu is None:
                return "escritura SIN dato en el bus (se guarda basura)", "Z"
            self.celdas[direccion] = dato_cpu & (2 ** self.m - 1)
            return "escritura", "Z"
        if oe_n == 0:
            if dato_cpu is not None:
                return "CONFLICTO: memoria y procesador manejan el bus", "?"
            return "lectura", self.celdas[direccion]
        return "habilitada pero sin salida (OE̅ = 1)", "Z"


mem = Memoria(4, 8)                                       # 16 palabras de 8 bits (16 x 8)
print(f"memoria 2^{mem.n} x {mem.m}: {2 ** mem.n} palabras, {2 ** mem.n * mem.m} bits, {2 ** mem.n * mem.m // 8} bytes")
secuencia = [
    ("escribir 0xA5 en 3",       3,  0, 1, 0, 0xA5),
    ("escribir 0x3C en 4",       4,  0, 1, 0, 0x3C),
    ("leer la dirección 3",      3,  0, 0, 1, None),
    ("leer la dirección 4",      4,  0, 0, 1, None),
    ("leer con CS̅ = 1",          3,  1, 0, 1, None),
    ("leer con OE̅ = 1",          3,  0, 1, 1, None),
    ("leer mientras CPU maneja", 3,  0, 0, 1, 0xFF),
    ("escribir sin manejar bus", 5,  0, 1, 0, None),
]
for texto, d, cs, oe, we, cpu in secuencia:
    modo, bus = mem.ciclo(d, cs, oe, we, cpu)
    valor = f"0x{bus:02X}" if isinstance(bus, int) else bus
    print(f"{texto:26s} dir={d:2d} CS̅={cs} OE̅={oe} WE̅={we} -> {modo:48s} bus: {valor}")

Las dos escrituras guardan 0xA5 en la dirección 3 y 0x3C en la 4, y las lecturas los devuelven. Con CS̅ = 1 o con OE̅ = 1 el bus queda en Z: la memoria no habla. Si el procesador maneja el bus mientras la memoria está leyendo, el simulador marca un conflicto. Y una escritura sin dato en el bus guarda basura: en una memoria real, el valor que haya en las líneas flotantes.

Paso 2: organización y capacidad

from math import log2

chips = [
    ("6116 (SRAM)",      2 * 1024,  8),
    ("62256 (SRAM)",     32 * 1024, 8),
    ("27C256 (EPROM)",   32 * 1024, 8),
    ("28C256 (EEPROM)",  32 * 1024, 8),
    ("1 Mbit x 1",       1024 * 1024, 1),
    ("memoria 16K x 16", 16 * 1024, 16),
]
print("chip                 palabras  bits/pal  líneas A  bits totales   bytes   rango de direcciones")
for nombre, palabras, ancho in chips:
    n = int(log2(palabras))
    bits = palabras * ancho
    print(f"{nombre:20s} {palabras:8d}  {ancho:6d}  {n:8d}  {bits:12d}  {bits // 8:6d}   0x0000 - 0x{palabras - 1:04X}")

print()
print("cuántas líneas de dirección y de datos se necesitan para...")
for palabras, ancho in ((256, 8), (1000, 8), (65536, 16)):
    n = (palabras - 1).bit_length()
    print(f"  {palabras} palabras de {ancho} bits: {n} líneas de dirección (alcanzan {2 ** n} palabras), {ancho} de datos")

print()
n = 16
print(f"un bus de dirección de {n} bits direcciona 2^{n} = {2 ** n} posiciones = {2 ** n // 1024} KiB (si cada posición es 1 byte)")
print("diferencia KB frente a Kb:  32 KiB =", 32 * 1024, "bytes =", 32 * 1024 * 8, "bits (256 Kib)")

Los chips de 32K × 8 usan 15 líneas de dirección y tienen 262 144 bits = 32 768 bytes (el «256K» del nombre son kilobits). Para 1000 palabras hacen falta 10 líneas (alcanzan 1024); para 65 536 palabras de 16 bits, 16 líneas de dirección y 16 de datos. Un bus de dirección de 16 bits llega a 64 KiB. Agregá tus propias memorias a la lista.

Paso 3: ampliar en profundidad y en ancho

class Chip:
    def __init__(self, nombre, palabras, ancho):
        self.nombre, self.palabras, self.ancho = nombre, palabras, ancho
        self.datos = [0] * palabras

    def escribir(self, a, v):
        self.datos[a] = v & (2 ** self.ancho - 1)

    def leer(self, a):
        return self.datos[a]


# Ampliar en PROFUNDIDAD: dos 8K x 8 forman 16K x 8.  A13 elige el chip; A12..A0 van a los dos.
c0, c1 = Chip("U0", 8192, 8), Chip("U1", 8192, 8)


def escribir_16k(a, v):
    chip = c1 if (a >> 13) & 1 else c0                     # CS̅ de cada chip sale de A13
    chip.escribir(a & 0x1FFF, v)


def leer_16k(a):
    chip = c1 if (a >> 13) & 1 else c0
    return chip.leer(a & 0x1FFF)


for a in range(16384):
    escribir_16k(a, (a * 7 + 3) & 0xFF)
ok = all(leer_16k(a) == (a * 7 + 3) & 0xFF for a in range(16384))
print("16K x 8 con dos 8K x 8 (A13 selecciona): se lee lo que se escribió en las 16384 direcciones:", ok)
print(f"  U0 responde en 0x0000 - 0x1FFF, U1 responde en 0x2000 - 0x3FFF")
print(f"  dirección 0x1FFF -> chip {'U1' if (0x1FFF >> 13) & 1 else 'U0'}, posición 0x{0x1FFF & 0x1FFF:04X}")
print(f"  dirección 0x2000 -> chip {'U1' if (0x2000 >> 13) & 1 else 'U0'}, posición 0x{0x2000 & 0x1FFF:04X}")

# Ampliar en ANCHO: dos 8K x 8 forman 8K x 16.  Mismas direcciones y mismo CS̅; cada chip aporta 8 bits.
bajo, alto = Chip("bajo", 8192, 8), Chip("alto", 8192, 8)


def escribir_ancho(a, v16):
    bajo.escribir(a, v16 & 0xFF)
    alto.escribir(a, v16 >> 8)


def leer_ancho(a):
    return (alto.leer(a) << 8) | bajo.leer(a)


escribir_ancho(0x0123, 0xBEEF)
print()
print(f"8K x 16 con dos 8K x 8 en paralelo: escribo 0xBEEF en 0x0123 -> chip bajo = 0x{bajo.leer(0x123):02X}, chip alto = 0x{alto.leer(0x123):02X}, leo 0x{leer_ancho(0x123):04X}")

Con A13 como selector, las 16 384 direcciones se escriben y se leen sin error: 0x1FFF cae en la posición más alta de U0 y 0x2000 en la más baja de U1. En ancho, el dato de 16 bits 0xBEEF se reparte: el chip bajo guarda 0xEF y el alto 0xBE, ambos en la misma dirección. Son las dos formas de componer memorias: más palabras con un bit de dirección más, o más bits con chips en paralelo.

Paso 4: tiempos de lectura y de escritura

# Tiempos de lectura de una SRAM típica (en ns): el dato es válido cuando se cumplen TODAS las condiciones.
t_aa, t_acs, t_oe = 70, 70, 35          # acceso por dirección, acceso por CS̅, acceso por OE̅


def dato_valido(t_dir, t_cs, t_oe_baja):
    """Instante en que el dato queda estable en el bus, y qué condición lo limita."""
    candidatos = {"dirección": t_dir + t_aa, "CS̅": t_cs + t_acs, "OE̅": t_oe_baja + t_oe}
    limitante = max(candidatos, key=candidatos.get)
    return candidatos[limitante], limitante, candidatos


casos = {
    "todo a la vez (t = 0)":               (0, 0, 0),
    "la dirección llega 20 ns tarde":      (20, 0, 0),
    "CS̅ baja 10 ns tarde":                 (0, 10, 0),
    "OE̅ llega muy tarde (60 ns)":          (0, 0, 60),
}
for texto, (td, tc, to) in casos.items():
    t, lim, c = dato_valido(td, tc, to)
    print(f"{texto:34s} dato válido a los {t:3d} ns (limita: {lim:9s}) | " + ", ".join(f"{k} {v}" for k, v in c.items()))

print()
t_ciclo = 85                              # tiempo mínimo entre accesos
f = 1e9 / t_ciclo
print(f"ciclo de {t_ciclo} ns -> {f / 1e6:.1f} millones de accesos por segundo")
for ancho in (8, 16, 32):
    print(f"  palabra de {ancho:2d} bits: {f * ancho / 8 / 1e6:6.1f} MB/s")

print()
# Escritura: el dato debe estar estable antes de que WE̅ suba y mantenerse un poco después.
t_wp, t_dw, t_dh = 50, 30, 0              # ancho mínimo de WE̅, setup del dato respecto de la subida de WE̅, hold del dato
we_baja, we_sube, dato_listo, dato_hasta = 10, 70, 45, 75
print("verificación de una escritura:")
print("  ancho de WE̅ =", we_sube - we_baja, "ns (mínimo", t_wp, ")", "OK" if we_sube - we_baja >= t_wp else "MALO")
print("  dato estable", we_sube - dato_listo, "ns antes de la subida de WE̅ (mínimo", t_dw, ")", "OK" if we_sube - dato_listo >= t_dw else "MALO")
print("  dato se mantiene", dato_hasta - we_sube, "ns después (mínimo", t_dh, ")", "OK" if dato_hasta - we_sube >= t_dh else "MALO")

Con todas las señales a la vez, el dato es válido a los 70 ns, limitado por la dirección. Si la dirección llega 20 ns tarde, llega a los 90 ns; si CS̅ baja 10 ns tarde, a los 80; si OE̅ llega a los 60 ns, el dato esperará hasta los 95 ns. Con un ciclo mínimo de 85 ns se obtienen 11,8 millones de accesos por segundo, es decir 11,8 MB/s con palabras de 8 bits y el doble con 16. La escritura de ejemplo falla: el pulso de WE̅ (60 ns) y el hold alcanzan, pero el dato está estable solo 25 ns antes de la subida y el mínimo es 30.

Los cuatro bloques muestran que la memoria es un modelo simple (dirección, dato, tres señales) con reglas de tiempo estrictas, y que un sistema se compone con pocos bloques regulares.

09 · Exploración interactiva

El laboratorio: una RAM con bus de datos

El simulador implementa una RAM de tamaño elegible (8 × 4, 16 × 4 o 32 × 8) con un bus de dirección, un bus de datos compartido con el procesador y las tres señales de control. Las casillas representan el nivel de cada pin (marcada = 1, desmarcada = 0). Elegís la dirección, el dato, las señales y si el procesador maneja el bus, y «Ejecutar ciclo» aplica esa combinación. Los botones de lectura y escritura arman directamente los ciclos típicos. La tabla de la izquierda muestra todas las palabras con la dirección elegida resaltada.

RAM con CS̅, OE̅, WE̅ y bus de datos compartido

Contenido de la memoria simulada Cada fila es una palabra con su dirección en binario y hexadecimal y su contenido. La dirección elegida está resaltada en verde y la última accedida en naranja. A la derecha se muestran la dirección, las señales de control y el valor del bus de datos.
Capacidad—
Líneas—
Modo del último ciclo—
Bus de datos—
Contenido de la dirección elegida—
Lecturas0
Escrituras0
Conflictos de bus0

Memoria de 16 × 4 con contenido aleatorio. Escribí un dato de 4 bits, elegí una dirección y presioná «Ciclo de escritura».

Últimos 8 ciclos desde el inicio de la simulación.
N.ºDirecciónCS̅/OE̅/WE̅Dato del procesadorModoBus de datos
Sin ciclos ejecutados.

Recorrido guiado

  1. Con 16 × 4, poné la dirección 3 y el dato 1010, y presioná «Ciclo de escritura»: se guarda 1010 en la dirección 3 (la fila queda resaltada). El bus de datos lo maneja el procesador.
  2. Presioná «Ciclo de lectura» con la misma dirección: el bus de datos pasa a mostrar 1010, ahora manejado por la memoria. El procesador dejó de manejar el bus.
  3. Cambiá la dirección a 7 y leé: aparece un valor cualquiera. Una RAM recién encendida tiene contenido indefinido (por eso «Borrar a cero» existe).
  4. Marcá CS̅ = 1 y ejecutá el ciclo con las señales actuales (OE̅ = 0, WE̅ = 1 desde la lectura): el bus queda en Z; la memoria ignora todo.
  5. Con CS̅ = 0, OE̅ = 0 y WE̅ = 1, marcá «El procesador maneja el bus de datos» y ejecutá el ciclo: «CONFLICTO de bus». Cada conflicto se cuenta.
  6. Desmarcá el procesador, poné WE̅ = 0 y OE̅ = 1 y ejecutá: la memoria escribe «basura» porque nadie manejaba las líneas de datos.
  7. Cambiá la organización a 8 × 4 (3 líneas de dirección, 32 bits) y a 32 × 8 (5 líneas, 256 bits = 32 bytes): el panel recalcula la capacidad y las líneas.
  8. Escribí tres datos distintos en tres direcciones y leelos en otro orden: el acceso es aleatorio, la dirección elegida no importa el orden.
Desafío: la dirección que no existe

Con 16 × 4, la dirección más alta es 15. Una dirección 16 necesitaría una quinta línea. Si un procesador con 5 líneas de dirección se conecta a este chip dejando A4 sin conectar al chip, las direcciones 0 y 16, 1 y 17, etc., caen en la misma palabra: la memoria se «repite» (mirroring) en el mapa. Es habitual en sistemas reales y la causa de errores fáciles de ver: escribir en la dirección 3 y leer el mismo dato en la 19.

10 · Comprensión

Confusiones frecuentes

«Una memoria de 1000 palabras necesita 1000 líneas de dirección»

Necesita 10: con n líneas se distinguen 2n palabras, y 210 = 1024 ≥ 1000. Las direcciones se codifican en binario.

«256K son 256 000 bytes»

K es 1024 y, en el nombre de un chip, la «K» suele ser kilobits. Un chip «256K» de 32K × 8 son 262 144 bits = 32 768 bytes.

«Los pines de datos de una RAM son siempre entradas»

Son bidireccionales: entradas en la escritura, salidas en la lectura, y alta impedancia el resto del tiempo.

«Con CS̅ = 0 la memoria siempre entrega el dato»

Solo con OE̅ = 0 (y WE̅ = 1). Habilitar el chip no habilita la salida.

«La memoria guarda el dato en cuanto baja WE̅»

En muchas memorias, el dato se captura en la subida de WE̅ (o en el último de CS̅ y WE̅ en subir); el dato y la dirección deben ser estables antes de esa subida y mantenerse un poco después.

«Se puede cambiar la dirección mientras WE̅ está bajo»

Puede escribirse el dato en una dirección equivocada o en varias. La dirección debe estar estable durante todo el pulso.

«Dos chips en el mismo bus no pueden coexistir»

Pueden, si solo uno tiene CS̅ activo en cada momento. Los demás quedan en alta impedancia.

«Más líneas de dirección implican más ancho de palabra»

Son cosas independientes: las líneas de dirección fijan cuántas palabras hay; las de datos, cuántos bits tiene cada una.

11 · Práctica guiada

Predecí antes de simular

1. Capacidad y líneas

Una memoria tiene 12 líneas de dirección y 8 de datos. ¿Cuántas palabras, cuántos bits y cuántos bytes almacena? ¿Cuál es la dirección más alta en hexadecimal?

Ver solución

212 = 4096 palabras (4 K), de 8 bits: 32 768 bits = 4096 bytes. La dirección más alta es 4095 = 0xFFF.

2. Cuántas líneas hacen falta

Necesitás guardar 3000 palabras de 16 bits. ¿Cuántas líneas de dirección y de datos? ¿Cuántas palabras quedan sin usar?

Ver solución

211 = 2048 es poco; 212 = 4096 alcanza: 12 líneas de dirección y 16 de datos. Quedan 4096 − 3000 = 1096 palabras sin usar.

3. Leer los modos

¿Qué modo corresponde a CS̅ = 0, OE̅ = 0, WE̅ = 1? ¿Y a CS̅ = 0, OE̅ = 1, WE̅ = 0? ¿Y a CS̅ = 1 con cualquier valor?

Ver solución

CS̅ = 0, OE̅ = 0, WE̅ = 1: lectura (la memoria maneja el bus). CS̅ = 0, OE̅ = 1, WE̅ = 0: escritura (el procesador maneja el bus). CS̅ = 1: chip deshabilitado y datos en alta impedancia, sin importar OE̅ ni WE̅.

4. Ampliar

Se quiere una memoria de 32K × 16 con chips de 16K × 8. ¿Cuántos chips y cómo se combinan? ¿Qué línea de dirección elige cada par?

Ver solución

Se necesitan 32K / 16K = 2 filas en profundidad y 16 / 8 = 2 columnas en ancho: 4 chips. Dos pares de chips (uno con los 8 bits bajos y otro con los 8 altos), cada par compartiendo su CS̅. Las 14 líneas bajas (A0 a A13) van a todos; la línea A14 elige entre los dos pares.

5. Tiempo de acceso

Una SRAM tiene tAA = 55 ns, tACS = 55 ns y tOE = 25 ns. La dirección es válida en t = 0, CS̅ baja en t = 5 ns y OE̅ baja en t = 40 ns. ¿Cuándo es válido el dato?

Ver solución

Por dirección: 0 + 55 = 55 ns. Por CS̅: 5 + 55 = 60 ns. Por OE̅: 40 + 25 = 65 ns. El dato es válido en el máximo: 65 ns, limitado por OE̅.

6. Diagnóstico

Un sistema escribe 0x55 en la dirección 0x10 y la lectura posterior da 0x55, pero a veces, si se escribe en 0x10 y enseguida en 0x11, el dato 0x55 también aparece en 0x11. ¿Qué falla probablemente?

Ver solución razonada

La dirección cambia (de 0x10 a 0x11) antes de que WE̅ suba, o la dirección llega al chip con un retardo distinto al de WE̅. La memoria ve durante un instante la nueva dirección con WE̅ todavía bajo y copia el dato también allí. Se corrige asegurando que la dirección esté estable (setup y hold) respecto del flanco de subida de WE̅, y que la señal WE̅ se retire antes de cambiar la dirección.

12 · Síntesis

Ideas para recordar

  • Una memoria de 2n × m guarda 2n palabras de m bits; usa n líneas de dirección y m de datos. Su capacidad es 2n · m bits.
  • Internamente es un banco de registros con un decodificador (para elegir cuál escribir) y un multiplexor (para elegir cuál leer); en los chips se organiza en matriz de filas y columnas.
  • Tres buses: dirección (hacia la memoria), datos (bidireccional, de tres estados) y control (CS̅, OE̅, WE̅, activos en bajo).
  • CS̅ = 1 deshabilita el chip; CS̅ = 0, OE̅ = 0, WE̅ = 1 es lectura; CS̅ = 0 y WE̅ = 0 es escritura. OE̅ y el procesador nunca deben manejar el bus de datos a la vez.
  • Lectura: el dato es válido cuando se cumple el último de tAA, tACS y tOE. Escritura: pulso mínimo de WE̅, dato y dirección estables antes y después de su subida.
  • Ampliar en ancho: chips en paralelo con las mismas direcciones; en profundidad: chips con CS̅ distintos, seleccionados por las líneas de dirección altas.
  • K, M y G son potencias de dos; «Kb» (bits) no es «KB» (bytes); una cantidad de palabras que no es potencia de dos deja posiciones sin usar.
  • Para el programa, la memoria es un arreglo de bytes; los punteros son direcciones y las variables multibyte ocupan posiciones consecutivas (endianness).

¿Listo para el tema 27?

Antes de avanzar, calculá de memoria cuántas líneas de dirección tiene una memoria de 8K × 8, explicá qué hacen CS̅, OE̅ y WE̅ y por qué el bus de datos necesita salidas de tres estados. Si podés justificar las tres respuestas, ya podés mirar cómo se guarda cada bit.

En el próximo tema veremos la RAM: las celdas de la SRAM y de la DRAM, por qué se pierde el contenido sin alimentación, qué es el refresco y cómo cambian los tiempos de acceso.