01 · Punto de partida
Tres detectores para el mismo patrón
La entrada x llega un bit por flanco de reloj. La salida y debe valer 1 cuando los últimos cuatro bits recibidos son 1, 0, 1, 1 (en ese orden, el 1 más antiguo primero). Los patrones pueden solaparse: en la entrada 1011011 el patrón termina dos veces, porque el último 1 de la primera aparición es el primer 1 de la segunda. Vamos a resolverlo de tres maneras, para ver qué diferencia hay entre pensar en estados y pensar en ventanas:
| Montaje | Cómo detecta | Chips | Qué aprendés |
|---|---|---|---|
| 1. Detector de Mealy | Una máquina de 4 estados; la salida está en la transición final | 74HC74 + compuertas | Método de sufijos, síntesis, asignación de estados |
| 2. Salida registrada | El mismo detector con un flip-flop más en la salida | + 74HC74 | Mealy registrada = Moore, un ciclo de latencia |
| 3. Ventana de desplazamiento | Un registro que guarda los últimos 4 bits y un comparador | 74HC164 + compuertas | Otra solución, más simple de diseñar y más cara en flip-flops |
Como en los talleres anteriores, no hace falta tener los materiales: el código construye y verifica la máquina, y el laboratorio permite probar otros patrones.
02 · Preparación
Materiales y herramientas
| Cant. | Componente | Para qué sirve |
|---|---|---|
| 2 | 74HC74 (dos flip-flops D con preset y clear) | Estados (montaje 1) y salida registrada (montaje 2) |
| 1 | 74HC164 (registro de desplazamiento serie-paralelo de 8 bits) | Ventana de bits (montaje 3) |
| 1 + 1 + 1 | 74HC04, 74HC08, 74HC32 (inversores, AND, OR) | Lógica de próximo estado, salida y comparador |
| 1 | 74HC00 y conmutador SPDT | Reloj manual sin rebotes (tema 7) |
| 1 | Interruptor deslizante SPDT | Entrada x (a +5 V o a masa) |
| 1 + 1 | Resistencia de 100 kΩ y condensador de 1 µF | Reset de encendido (tema 10) |
| 3 a 4 | LED con resistencia de 1 kΩ | Mostrar y, los estados y la ventana |
| 5 | Condensador de 100 nF | Desacople, uno por chip |
| 1 | Protoboard, cables y fuente regulada de 5 V | Montaje y alimentación |
Con un reloj manual todo ocurre despacio, y alcanza con los LED y el multímetro. Antes de armar, anotá la tabla de estados en papel: los errores de este taller casi siempre están en la tabla, no en los cables.
Antes de dar tensión
- 100 nF por chip; ninguna entrada al aire, incluidas las compuertas y los flip-flops sin usar.
- PRE̅ de los flip-flops en +5 V; CLR̅ conectado a la red de reset (los dos flip-flops de estado deben arrancar en 00).
- La entrada x puede cambiar en cualquier momento de la mano, pero solo se la muestrea en el flanco del reloj manual.
03 · Del patrón a la máquina
De un patrón a una máquina de estados
La idea para no equivocarse es dar a cada estado un significado preciso: «los últimos k bits recibidos coinciden con los primeros k bits del patrón», tomando el k más grande posible (el sufijo más largo que es prefijo del patrón). Para el patrón 1011:
| Estado | Bits recibidos al final | Falta para completar |
|---|---|---|
| S0 | nada útil (termina en 0 sin comenzar el patrón) | 1011 completo |
| S1 | …1 | 011 |
| S2 | …10 | 11 |
| S3 | …101 | 1 |
Cada transición se calcula con una pregunta: «agrego el bit x a lo que ya tengo; ¿cuál es el sufijo más largo del resultado que es un prefijo del patrón?». Así salen todas las flechas, incluidas las de retroceso:
| Estado | x = 0 | x = 1 | Por qué |
|---|---|---|---|
| S0 | S0 / 0 | S1 / 0 | Un 1 comienza el patrón; un 0 no ayuda |
| S1 | S2 / 0 | S1 / 0 | «10» avanza; «11» deja solo el último 1 como prefijo |
| S2 | S0 / 0 | S3 / 0 | «101» avanza; «100» no coincide con nada |
| S3 | S2 / 0 | S1 / 1 | «1011» es el patrón: salida 1, y el último 1 vale como inicio del siguiente; «1010» deja «10» |
Dos observaciones: ningún estado se «cuelga» (cada uno tiene una flecha por cada valor de x, por lo que la tabla es completa), y las flechas de retroceso (S1 con 1, S2 con 0, S3 con 0) son las que más se olvidan: llevan a un estado intermedio, no al inicial. Sin solapamiento, S3 con x = 1 volvería a S0.
04 · Montaje 1
El detector de Mealy
Con 4 estados alcanzan 2 flip-flops. La asignación de códigos importa: con la «natural» (S0 = 00, S1 = 01, S2 = 10, S3 = 11) salen 9 literales; probando las 24 asignaciones posibles, la mejor da solo 7:
| Estado | Código Q1 Q0 | x | Próximo Q1⁺ Q0⁺ | y |
|---|---|---|---|---|
| S0 | 00 | 0 | 00 | 0 |
| S0 | 00 | 1 | 11 (S1) | 0 |
| S1 | 11 | 0 | 01 (S2) | 0 |
| S1 | 11 | 1 | 11 (S1) | 0 |
| S2 | 01 | 0 | 00 (S0) | 0 |
| S2 | 01 | 1 | 10 (S3) | 0 |
| S3 | 10 | 0 | 01 (S2) | 0 |
| S3 | 10 | 1 | 11 (S1) | 1 |
Minimizando (los cuatro estados usan las cuatro combinaciones: no hay estados sin usar ni «no importa»):
D1 = x significa que el flip-flop Q1 simplemente guarda el último bit recibido, y el término ~Q0 · x aparece en D0 y en y: se calcula una vez y se reutiliza.
| Conexión | Función |
|---|---|
| x → U1 pin 2 (1D) | D1 = x |
| U1 pin 5 (1Q) = Q1; pin 9 (2Q) = Q0 | Bits del estado |
| U1 pin 9 (Q0) → U2 pin 1 → pin 2 (~Q0) | Inversor |
| ~Q0 (U2 pin 2) y x → U3 pines 1 y 2 → pin 3 (t = ~Q0·x) | Término compartido |
| Q1 (U1 pin 5) y t → U3 pines 4 y 5 → pin 6 | y = Q1 · t: la salida, hacia un LED |
| Q1 y t → U4 pines 1 y 2 → pin 3 → U1 pin 12 (2D) | D0 = Q1 + t |
| Reloj manual → U1 pines 3 y 11 | Un reloj para los dos flip-flops |
| U1 pines 1 y 13 (CLR̅) ← red de reset; pines 4 y 10 (PRE̅) a +5 V | Estado inicial S0 = 00 |
| Alimentación y 100 nF en cada chip; entradas libres de U2, U3 y U4 a masa | Sin entradas al aire |
Pruebas
| Pulso | x | Estado antes | Estado después | y durante el ciclo |
|---|---|---|---|---|
| 1 | 1 | S0 (00) | S1 (11) | 0 |
| 2 | 0 | S1 (11) | S2 (01) | 0 |
| 3 | 1 | S2 (01) | S3 (10) | 0 |
| 4 | 1 | S3 (10) | S1 (11) | 1 |
| 5 | 0 | S1 (11) | S2 (01) | 0 |
| 6 | 1 | S2 (01) | S3 (10) | 0 |
| 7 | 1 | S3 (10) | S1 (11) | 1 |
Con el interruptor de x puesto, el LED de y se enciende antes del flanco del pulso 4 (en cuanto x = 1 con el circuito en S3): es una salida de Mealy y depende de la entrada del mismo ciclo. Si movés x con el reloj quieto, el LED sigue a x.
05 · Montaje 2
Salida registrada (Moore)
Si la salida y va a alimentar otro circuito, conviene que no cambie fuera del flanco (tema 23). La solución es pasarla por un flip-flop: yreg se carga con el valor de y en cada flanco. El conjunto se comporta como una máquina de Moore: la detección aparece un ciclo después del último bit.
| Conexión | Función |
|---|---|
| y (U3 pin 6) → U5 pin 2 (1D) | La salida Mealy entra al flip-flop de salida |
| Reloj manual → U5 pin 3 (1CLK) | El mismo reloj |
| U5 pin 5 (1Q) → LED con 1 kΩ | yreg: la salida limpia |
| U5 pines 1 y 4 (CLR̅, PRE̅) → CLR̅ a la red de reset, PRE̅ a +5 V | Arranca en 0 |
| U5 pines 10, 11, 12 y 13 | Segundo flip-flop sin usar: PRE̅ y CLR̅ a +5 V, CLK y D a masa |
Pruebas
- Misma secuencia 1 0 1 1 0 1 1: y (Mealy) se enciende antes del flanco de los pulsos 4 y 7; yreg se enciende después de esos flancos y dura hasta el siguiente.
- Mover x con el reloj quieto: y (Mealy) cambia; yreg no se mueve.
- Fidelidad: con cualquier secuencia, yreg es y desplazada exactamente un ciclo.
06 · Montaje 3
Ventana de desplazamiento
La otra forma de pensarlo es mucho más directa: guardar los últimos 4 bits en un registro de desplazamiento y comparar el contenido con 1011. No hay estados que diseñar, ni tabla, ni retrocesos que olvidar: el registro ya es la memoria. El 74HC164 (tema 14) recibe x en DSA, desplaza en cada flanco y entrega los bits en Q0 (el más nuevo) a Q3 (el más viejo). Para 1011 (el 1 más viejo en Q3):
| Conexión | Función |
|---|---|
| U6 pin 14 / pin 7 | +5 V / masa, con 100 nF entre ambos |
| x → U6 pin 1 (DSA); pin 2 (DSB) a +5 V | Entrada serie: DSA · DSB = x |
| Reloj manual → U6 pin 8 (CP) | Desplaza en cada flanco |
| U6 pin 9 (MR̅) ← red de reset | Pone el registro en 0000 al encender |
| U6 pin 5 (Q2) → inversor → ~Q2 | El segundo bit más viejo debe ser 0 |
| Q1 (pin 4) y Q0 (pin 3) → AND → a | a = Q1 · Q0 |
| Q3 (pin 6) y ~Q2 → AND → b | b = Q3 · ~Q2 |
| a y b → AND → yventana → LED | y = Q3 · ~Q2 · Q1 · Q0 |
| U6 pines 10 a 13 (Q4 a Q7) | Sin conectar (salidas libres) |
Pruebas y comparación
- Reset y 1 0 1 1: con cuatro flancos, los LED de Q3..Q0 muestran 1011 y y se enciende, durante el ciclo posterior al cuarto flanco.
- Solapamiento: con 1 0 1 1 0 1 1, detecta las dos apariciones; el registro lo hace natural, porque siempre mira la ventana completa.
- Mismo comportamiento que el montaje 2: yventana coincide cuadro por cuadro con yreg.
- Costo: 4 flip-flops y un comparador de 4 bits, contra 2 flip-flops y 5 compuertas de la máquina de estados. Para un patrón de 16 bits: 16 flip-flops contra 4.
07 · Cuando no funciona
Verificación, fallas y método
La mejor forma de verificar un detector es compararlo con una referencia sencilla: una búsqueda directa del patrón en la entrada completa. Si ambos coinciden sobre miles de bits aleatorios, la tabla es correcta, y los casos de borde (patrón solapado, comienzo de la entrada, ceros largos) quedan cubiertos casi gratis. El código de la sección siguiente lo hace.
| Síntoma | Causa probable | Cómo comprobarlo |
|---|---|---|
| Detecta 1011 pero no 1011011 (la segunda vez) | La transición S3 con x = 1 volvió a S0 (sin solapamiento) | Revisar el destino de la fila (S3, 1): debe ser S1 |
| Detecta cuando aparece 101 o 11 | Falta una condición en la ecuación de y, o la compuerta de y no recibe todas las señales | Medir cada entrada de la AND con una secuencia conocida |
| Falla después de un 0 en medio del patrón (10011) | Un retroceso incorrecto: «100» debe volver a S0, no a S1 ni S2 | Recorrer la tabla con 1 0 0 1 1 y ver el estado en cada paso |
| Con la misma entrada, a veces detecta y a veces no | Estado inicial indefinido (falta el reset) o rebotes en el reloj manual | Reiniciar y repetir; usar el reloj con latch |
| y parpadea al mover x con el reloj quieto | Salida de Mealy: es el comportamiento esperado | Usar la versión con salida registrada (montaje 2) |
| La versión registrada se retrasa dos ciclos | Se registró la salida dos veces o se usó el reloj invertido | Un solo flip-flop de salida con el mismo reloj |
| La ventana detecta apenas se enciende | MR̅ del 74HC164 sin conectar: arranca con basura | Medir el pin 9 al encender: debe estar en bajo unos milisegundos |
| La ventana no detecta nunca | Orden invertido: el patrón se comparó al revés (Q0 como bit más viejo) | Escribir 1 0 1 1 y mirar en qué LED queda cada bit |
Método de armado por etapas
- La tabla primero: escribirla y recorrerla con 3 o 4 secuencias antes de tocar el protoboard.
- El reloj manual y los flip-flops con las entradas fijas, antes de la lógica.
- La lógica de próximo estado y los LED de los dos bits de estado, probando la secuencia de la sección 4.
- La salida y al final, comprobándola con los casos de solapamiento.
- Después, el flip-flop de salida y por último la ventana, comparando las tres salidas con la misma entrada.
08 · El taller en código
Construir, verificar, sintetizar y comparar
Los bloques generan el autómata de cualquier patrón, lo comparan con una búsqueda directa, buscan la mejor asignación de estados para el patrón 1011 y miden la equivalencia entre la máquina de estados y la ventana de desplazamiento.
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 autómata a partir del patrón
def construir(patron, solapado=True):
"""Autómata de Mealy: el estado k significa 'los últimos k bits coinciden con el comienzo del patrón'."""
L = len(patron)
def mayor_prefijo(texto):
for j in range(min(L, len(texto)), -1, -1):
if texto.endswith(patron[:j]):
return j
borde = mayor_prefijo(patron[1:]) if solapado else 0 # cuánto del final de un acierto se reutiliza
tabla = {}
for k in range(L):
for c in "01":
j = mayor_prefijo(patron[:k] + c)
if j == L:
tabla[(k, c)] = (borde, 1)
else:
tabla[(k, c)] = (j, 0)
return tabla
patron = "1011"
tabla = construir(patron)
print(f"patrón {patron}, con solapamiento: {len(tabla) // 2} estados (S0..S{len(tabla) // 2 - 1})")
print("estado x=0 x=1")
for k in range(len(patron)):
f0, f1 = tabla[(k, "0")], tabla[(k, "1")]
print(f" S{k} S{f0[0]} / {f0[1]} S{f1[0]} / {f1[1]}")
print()
for k in range(len(patron)):
print(f"S{k} = últimos bits '{patron[:k]}' (coinciden con el comienzo del patrón)")
print()
sin = construir(patron, solapado=False)
print("sin solapamiento, el estado S3 con x=1 va a:", sin[(3, "1")], "(vuelve a S0)")
print("con solapamiento, el estado S3 con x=1 va a:", tabla[(3, "1")], "(el último 1 ya es el comienzo del siguiente)")
La tabla generada coincide con la que dedujimos a mano: S0 va a S0 con 0 y a S1 con 1; S1, a S2 con 0 y a sí mismo con 1; S2, a S0 con 0 y a S3 con 1; y S3, a S2 con 0 y, con 1, a S1 con salida 1. Con solapamiento, el acierto vuelve a S1 (el último 1 sirve de inicio); sin solapamiento volvería a S0. Probá con otros patrones, como «110» o «10010».
Paso 2: contra una búsqueda directa
import random
def construir(patron, solapado=True):
L = len(patron)
def mayor_prefijo(texto):
for j in range(min(L, len(texto)), -1, -1):
if texto.endswith(patron[:j]):
return j
borde = mayor_prefijo(patron[1:]) if solapado else 0
tabla = {}
for k in range(L):
for c in "01":
j = mayor_prefijo(patron[:k] + c)
tabla[(k, c)] = (borde, 1) if j == L else (j, 0)
return tabla
def detectar(tabla, bits):
k, salidas = 0, []
for b in bits:
k, y = tabla[(k, b)]
salidas.append(y)
return salidas
def buscar(patron, bits, solapado=True):
"""Referencia: búsqueda directa de las posiciones donde TERMINA el patrón."""
L, fin, i, res = len(patron), -1, 0, []
while i + L <= len(bits):
if bits[i:i + L] == patron:
res.append(i + L - 1)
i += 1 if solapado else L
else:
i += 1
return res
rnd = random.Random(25)
bits = "".join(rnd.choice("01") for _ in range(2000))
for solapado in (True, False):
t = construir("1011", solapado)
y = detectar(t, bits)
pos_fsm = [i for i, v in enumerate(y) if v]
pos_ref = buscar("1011", bits, solapado)
print(f"solapado={solapado}: FSM {len(pos_fsm)} detecciones, referencia {len(pos_ref)}, coinciden: {pos_fsm == pos_ref}")
muestra = "0010110110011011"
y = detectar(construir("1011"), muestra)
print()
print("entrada :", muestra)
print("salida y:", "".join(str(v) for v in y))
print("el patrón termina en las posiciones:", [i for i, v in enumerate(y) if v])
Sobre 2000 bits aleatorios, la máquina detecta 115 apariciones con solapamiento, las mismas posiciones que la búsqueda directa; sin solapamiento son 105, también coincidentes. En la muestra de 16 bits el patrón termina en las posiciones 5, 8 y 15 (contando desde 0): 001011 | 011 | 0011011, con la segunda aparición solapada con la primera. Una discrepancia con la referencia sería la señal de que la tabla tiene un error.
Paso 3: la mejor asignación de estados
def primos(minterms, dontcares):
grupos = {(m, 0) for m in set(minterms) | set(dontcares)}
primos = set()
while grupos:
siguientes, usados = set(), set()
lista = list(grupos)
for i in range(len(lista)):
for j in range(i + 1, len(lista)):
(v1, m1), (v2, m2) = lista[i], lista[j]
d = v1 ^ v2
if m1 == m2 and d and d & (d - 1) == 0:
siguientes.add((v1 & ~d, m1 | d))
usados.update((lista[i], lista[j]))
primos |= grupos - usados
grupos = siguientes
return primos
def cubre(p, m):
v, mask = p
return (m & ~mask) == v
def minimizar(minterms, dontcares, nombres):
n = len(nombres)
if not minterms:
return "0"
ps = primos(minterms, dontcares)
faltan, elegidos = set(minterms), []
while faltan:
esencial = next((p for m in sorted(faltan) for p in [[q for q in ps if cubre(q, m)]] if len(p) == 1), None)
mejor = esencial[0] if esencial else max(ps, key=lambda p: sum(cubre(p, m) for m in faltan))
elegidos.append(mejor)
faltan -= {m for m in faltan if cubre(mejor, m)}
terminos = []
for v, mask in elegidos:
lits = []
for i in range(n - 1, -1, -1):
if not (mask >> i) & 1:
lits.append(nombres[n - 1 - i] if (v >> i) & 1 else "~" + nombres[n - 1 - i])
terminos.append("·".join(lits) if lits else "1")
return " + ".join(sorted(terminos))
from itertools import permutations
# Mealy del detector 1011 con solapamiento (estados S0..S3), entrada x.
TABLA = {
(0, 0): (0, 0), (0, 1): (1, 0),
(1, 0): (2, 0), (1, 1): (1, 0),
(2, 0): (0, 0), (2, 1): (3, 0),
(3, 0): (2, 0), (3, 1): (1, 1),
}
NOMBRES = ["Q1", "Q0", "x"]
def ecuaciones(cod):
d1, d0, y = [], [], []
for (s, x), (prox, sal) in TABLA.items():
idx = cod[s] * 2 + x
if cod[prox] >> 1: d1.append(idx)
if cod[prox] & 1: d0.append(idx)
if sal: y.append(idx)
return [minimizar(d1, [], NOMBRES), minimizar(d0, [], NOMBRES), minimizar(y, [], NOMBRES)]
def literales(eqs):
return sum(e.count("Q") + e.count("x") for e in eqs)
natural = {0: 0, 1: 1, 2: 2, 3: 3}
eq_nat = ecuaciones(natural)
print("asignación natural S0=00 S1=01 S2=10 S3=11:")
for n, e in zip(("D1", "D0", "y "), eq_nat):
print(f" {n} = {e}")
print(f" literales: {literales(eq_nat)}")
resultados = []
for perm in permutations(range(4)):
cod = dict(enumerate(perm))
eqs = ecuaciones(cod)
resultados.append((literales(eqs), perm, eqs))
resultados.sort()
mejor_l, mejor_p, mejor_eq = resultados[0]
print()
print("de las 24 asignaciones posibles, la mejor:")
print(" códigos:", {f"S{s}": format(c, "02b") for s, c in enumerate(mejor_p)})
for n, e in zip(("D1", "D0", "y "), mejor_eq):
print(f" {n} = {e}")
print(f" literales: {mejor_l} (peor asignación: {resultados[-1][0]})")
# Verificación del circuito contra la tabla (con los códigos ganadores)
cod = dict(enumerate(mejor_p))
inv = {v: k for k, v in cod.items()}
def circuito(q1, q0, x): # D1 = x, D0 = Q1 + ~Q0·x, y = Q1·~Q0·x
return x, q1 | ((1 - q0) & x), q1 & (1 - q0) & x
ok = True
for (s, x), (prox, sal) in TABLA.items():
q1, q0 = cod[s] >> 1, cod[s] & 1
d1, d0, y = circuito(q1, q0, x)
ok &= (d1 * 2 + d0 == cod[prox]) and (y == sal)
print()
print("el circuito de 7 literales reproduce las 8 filas de la tabla:", ok)
Con los códigos «naturales» salen D1 = Q0·~x + Q1·~Q0·x, D0 = x e y = Q1·Q0·x, 9 literales. De las 24 asignaciones posibles, la mejor (S0 = 00, S1 = 11, S2 = 01, S3 = 10) da D1 = x, D0 = Q1 + ~Q0·x e y = Q1·~Q0·x, 7 literales; la peor llega a 18. El circuito resultante reproduce las 8 filas de la tabla. La asignación cambia el costo del circuito por un factor de más de dos, sin cambiar su comportamiento.
Paso 4: la ventana frente a la máquina de estados
import random
def construir(patron):
L = len(patron)
def mayor_prefijo(texto):
for j in range(min(L, len(texto)), -1, -1):
if texto.endswith(patron[:j]):
return j
borde = mayor_prefijo(patron[1:])
return {(k, c): ((borde, 1) if mayor_prefijo(patron[:k] + c) == L else (mayor_prefijo(patron[:k] + c), 0)) for k in range(L) for c in "01"}
def fsm_mealy(patron, bits):
t, k, ys = construir(patron), 0, []
for b in bits:
k, y = t[(k, b)]
ys.append(y)
return ys
def ventana(patron, bits):
"""Registro de desplazamiento con L etapas y un comparador: la salida depende solo del contenido del registro."""
L, reg, ys = len(patron), "0" * len(patron), []
for b in bits:
ys.append(int(reg == patron)) # lo que ve el comparador durante este ciclo (antes de que entre b)
reg = reg[1:] + b # flanco: el bit nuevo entra y el más viejo sale
return ys
rnd = random.Random(31)
for patron in ("1011", "110", "10010"):
bits = "".join(rnd.choice("01") for _ in range(3000))
ym, yv = fsm_mealy(patron, bits), ventana(patron, bits)
desfasada = ym[:-1] == yv[1:]
print(f"patrón {patron:6s}: Mealy {sum(ym):3d} detecciones | ventana {sum(yv):3d} | ventana == Mealy retrasada 1 ciclo: {desfasada}")
print()
print("recursos para un patrón de L bits:")
for L in (4, 8, 16):
print(f" L = {L:2d}: FSM con solapamiento {L} estados -> {max(1, (L - 1).bit_length())} flip-flops (+ lógica que cambia con el patrón)"
f" | ventana {L} flip-flops + comparador de {L} bits (el patrón se cambia recableando)")
Para tres patrones distintos y 3000 bits aleatorios cada uno, la ventana detecta exactamente las mismas apariciones que la máquina de Mealy, pero con un ciclo de retraso: la salida de la ventana depende del registro, no de la entrada actual. Es la relación Moore = Mealy retrasada de los temas 23 y 24. En recursos, la máquina de estados crece de forma logarítmica (2, 3 y 4 flip-flops para 4, 8 y 16 bits) y la ventana de forma lineal (4, 8 y 16), pero la ventana no necesita recalcular ninguna tabla.
Los cuatro bloques cubren el ciclo completo: construir la máquina, verificarla contra una referencia, optimizarla y compararla con otra arquitectura. Es el mismo ciclo con que se verifica software.
09 · Exploración interactiva
El banco de pruebas
El simulador construye el detector para el patrón que escribas (de 2 a 6 bits) y lo ejecuta en tres versiones: Mealy, Moore y ventana de desplazamiento. Podés enviar bits de a uno o aplicar una secuencia entera, y activar o no el solapamiento. La fila de abajo muestra las entradas, marca con un borde los lugares donde el patrón termina de verdad y con un triángulo los ciclos en que la salida vale 1: comparando ambos se ve la diferencia de tiempo entre los modelos.
Detector de secuencia configurable
Patrón 1011, Mealy con solapamiento. Enviá bits o la secuencia de ejemplo.
| N.º | x | Antes | Después | y en el ciclo |
|---|---|---|---|---|
| Sin bits aplicados. | ||||
Recorrido guiado
- Con Mealy y solapamiento, enviá 1, 0, 1, 1 con los botones: el estado recorre S1, S2, S3 y vuelve a S1; la salida y vale 1 en el cuarto bit, en el mismo ciclo.
- Enviá la secuencia de ejemplo 0010110110011011: la salida vale 1 en tres ciclos (posiciones 5, 8 y 15) y coincide con las «ocurrencias reales».
- Cambiá a Moore (se reinicia) y repetí: aparece un estado más (S4, «patrón completo») y las ▲ quedan un ciclo después de los bordes de la entrada. Las detecciones totales son iguales, pero la última puede quedar pendiente si la secuencia termina justo en el patrón.
- Cambiá a «Ventana de desplazamiento»: aparece el registro de 4 bits con cada bit comparado con el patrón; las ▲ coinciden con las de Moore.
- Desmarcá «Con solapamiento» (en Mealy o Moore) y enviá 1011011: sólo detecta la primera aparición, porque tras ella se vuelve a empezar de cero. La ventana siempre solapa.
- Probá el patrón 110 y la secuencia 1110110: observá cómo el estado S2 se queda en sí mismo mientras siguen llegando unos (S2 con x = 1 → S2).
- Probá un patrón de 6 bits como 101101: son 6 estados en Mealy (3 flip-flops) y 7 en Moore (3 flip-flops), contra 6 flip-flops de la ventana.
- Probá 000: la ventana, que arranca en 000, ya cumple el patrón en el primer ciclo sin que haya llegado ningún bit útil; la máquina de estados no. Es un caso de borde del reset.
Desafío: el patrón que se solapa consigo mismo
El patrón 1010 se solapa a sí mismo: en 101010, aparece en las posiciones 3 y 5. Construí su máquina con el simulador y mirá la fila de retroceso de S3 (cuando llega el último 0, la salida es 1 y el estado vuelve a S2, no a S0, porque «10» ya es un prefijo). El tamaño del solapamiento posible lo da el «borde» del patrón: para 1011 es 1 (el 1 final), para 1010 es 2 (el «10» final) y para 1000 es 0 (no hay solapamiento).
10 · Comprensión
Confusiones frecuentes
«Tras detectar el patrón, la máquina vuelve al estado inicial»
Solo si se pide sin solapamiento. Con solapamiento, vuelve al estado que corresponde al final de lo que acaba de llegar (S1 en 1011).
«Si un bit no coincide, se vuelve siempre a S0»
No: se vuelve al estado del sufijo más largo que sigue siendo prefijo del patrón. Con 1011, tras 101 y un 0 se pasa a S2 (queda «10»), no a S0.
«La salida de Mealy aparece un ciclo después del último bit»
Aparece en el mismo ciclo del último bit, porque depende de la entrada actual. Un ciclo después es la salida de Moore o la registrada.
«La ventana de desplazamiento y la máquina de estados no son equivalentes»
Detectan lo mismo: la ventana es la versión de Moore con la salida decodificada del contenido del registro. Cambian los recursos, no el comportamiento.
«Para cambiar el patrón de la máquina basta con cambiar un cable»
Hay que recalcular la tabla y las ecuaciones. En la ventana sí: basta con cambiar qué bits entran negados al comparador.
«Con L bits de patrón siempre hacen falta L flip-flops»
Con la máquina de estados, solo ⌈log2 L⌉ (o ⌈log2 (L + 1)⌉ en Moore). La ventana sí usa L.
«Un reset da lo mismo para las dos arquitecturas»
En la máquina de estados el reset debe llevar a S0; en la ventana, a ceros, lo que equivale a «los últimos bits fueron 0000» y puede hacer detectar patrones de ceros al arrancar.
«Si la prueba con 4 secuencias funciona, el detector está bien»
Los errores de retroceso aparecen solo con ciertas secuencias. Hay que comparar contra una referencia con miles de bits aleatorios, como en el paso 2 del código.
11 · Práctica guiada
Predecí antes de simular
1. Tabla de un patrón nuevo
Construí a mano la tabla del detector (Mealy, con solapamiento) del patrón 110.
Ver solución
Estados: S0 (nada), S1 («1»), S2 («11»). S0: 0 → S0/0, 1 → S1/0. S1: 0 → S0/0 («10» no es prefijo), 1 → S2/0. S2: 0 → S0/1 (patrón completo; el borde de 110 es 0, no hay solapamiento posible), 1 → S2/0 («111» deja «11»). Tres estados, 2 flip-flops.
2. Seguir el detector
El detector de 1011 (Mealy, con solapamiento) parte de S0 y recibe 0 1 1 0 1 1 1 0 1 1. ¿En qué ciclos vale y = 1?
Ver solución razonada
Estados: 0→S0, 1→S1, 1→S1, 0→S2, 1→S3, 1→S1 (y = 1 en el 6.º bit), 1→S1, 0→S2, 1→S3, 1→S1 (y = 1 en el 10.º). La salida vale 1 en los ciclos 6 y 10 (contando desde 1). Se confirma buscando 1011 en 0110111011: aparece terminando en el 6.º y el 10.º bit.
3. Cantidad de flip-flops
Un detector Mealy de un patrón de 12 bits, ¿cuántos estados y flip-flops usa? ¿Y en Moore? ¿Y una ventana?
Ver solución
Mealy: 12 estados → ⌈log2 12⌉ = 4 flip-flops. Moore: 13 estados → 4 flip-flops. Ventana: 12 flip-flops y un comparador de 12 bits.
4. Orden del patrón
En el montaje de la ventana con el 74HC164, ¿qué ecuación de y detecta el patrón 1101 (el 1 más viejo primero)?
Ver solución
El 1 más viejo está en Q3, luego Q2, Q1 y Q0 el más nuevo: Q3 = 1, Q2 = 1, Q1 = 0, Q0 = 1. Entonces y = Q3 · Q2 · ~Q1 · Q0. Cambiar el patrón solo cambia qué bits entran negados.
5. El borde de un patrón
¿Cuánto solapamiento admiten los patrones 1011, 1010 y 1000? Escribí el estado al que va el último acierto en cada caso.
Ver solución
1011: el sufijo «1» es prefijo, solapa 1 bit → el acierto vuelve a S1. 1010: el sufijo «10» es prefijo, solapa 2 bits → vuelve a S2. 1000: ningún sufijo propio es prefijo, solapa 0 → vuelve a S0.
6. Diagnóstico
Un detector de 1011 reconoce bien la entrada 1 0 1 1 aislada, pero no detecta nada en 1 0 1 0 1 1, donde el patrón aparece terminando en el último bit. ¿Qué transición está mal?
Ver solución razonada
Tras «101» (estado S3) llega un 0 y se forma «1010», cuyo sufijo «10» es un prefijo del patrón: el retroceso correcto es S3 con x = 0 → S2. Si esa flecha va a S0, el detector pierde el «10» y no puede completar «1011» con los dos últimos bits. Es el error clásico de retroceso, que una prueba con 1 0 1 1 aislada nunca encuentra y una comparación contra la referencia sobre muchas secuencias sí.
12 · Síntesis
Ideas para recordar
- Un detector de secuencia es una máquina de estados donde Sk significa «los últimos k bits coinciden con el comienzo del patrón»; cada transición va al sufijo más largo que sea prefijo.
- Las flechas de retroceso no vuelven al inicio sino al estado del mejor prefijo; con solapamiento, el acierto vuelve al estado del borde del patrón.
- Mealy: L estados, salida en la transición, en el mismo ciclo. Moore: L + 1 estados, salida un ciclo después. Una Mealy con salida registrada equivale a Moore.
- La asignación de estados cambia el costo: para 1011, 9 literales con códigos naturales y 7 con la mejor; probar las 24 asignaciones es barato.
- La ventana (registro de desplazamiento + comparador) detecta lo mismo con L flip-flops, sin tabla, pero consume más flip-flops cuando L crece.
- Se verifica comparando con una búsqueda directa sobre miles de bits aleatorios: ahí aparecen los errores de retroceso y de solapamiento.
- Reset: el estado inicial de la máquina y los ceros de la ventana; un patrón de ceros puede detectarse al arrancar en la ventana.
- Es el mismo método (autómata de coincidencias) que se usa en software para buscar cadenas.
¿Listo para el tema 26?
Antes de avanzar, construí a mano la tabla del detector de otro patrón de tres o cuatro bits, indicá cuántos flip-flops necesita en Mealy y en Moore y explicá por qué una salida registrada llega un ciclo tarde. Si podés justificar las tres respuestas, ya cerraste la parte de máquinas de estados.
En el próximo tema empieza el bloque de memorias: organización y buses, es decir cómo se direccionan palabras, cuál es la capacidad de una memoria y cómo se leen y escriben datos.