← Índice del curso

Circuitos Secuenciales y Memoria · Tema 25

Taller: detector de secuencia

Es el ejercicio que cierra la parte de máquinas de estados: un circuito que mira una entrada serie, bit a bit, y enciende una salida cuando acaba de pasar un patrón determinado, por ejemplo 1011. Usa todo lo de los temas 22 a 24: se especifica con un diagrama, se elige entre Mealy y Moore, se codifican los estados, se obtienen las ecuaciones y se arma con dos flip-flops. Y se compara con la solución de «fuerza bruta»: un registro de desplazamiento y un comparador.

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:

Los tres montajes del taller.
MontajeCómo detectaChipsQué aprendés
1. Detector de MealyUna máquina de 4 estados; la salida está en la transición final74HC74 + compuertasMétodo de sufijos, síntesis, asignación de estados
2. Salida registradaEl mismo detector con un flip-flop más en la salida+ 74HC74Mealy registrada = Moore, un ciclo de latencia
3. Ventana de desplazamientoUn registro que guarda los últimos 4 bits y un comparador74HC164 + compuertasOtra 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

Lista de materiales.
Cant.ComponentePara qué sirve
274HC74 (dos flip-flops D con preset y clear)Estados (montaje 1) y salida registrada (montaje 2)
174HC164 (registro de desplazamiento serie-paralelo de 8 bits)Ventana de bits (montaje 3)
1 + 1 + 174HC04, 74HC08, 74HC32 (inversores, AND, OR)Lógica de próximo estado, salida y comparador
174HC00 y conmutador SPDTReloj manual sin rebotes (tema 7)
1Interruptor deslizante SPDTEntrada x (a +5 V o a masa)
1 + 1Resistencia de 100 kΩ y condensador de 1 µFReset de encendido (tema 10)
3 a 4LED con resistencia de 1 kΩMostrar y, los estados y la ventana
5Condensador de 100 nFDesacople, uno por chip
1Protoboard, cables y fuente regulada de 5 VMontaje 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:

Significado de cada estado para el patrón 1011.
EstadoBits recibidos al finalFalta para completar
S0nada útil (termina en 0 sin comenzar el patrón)1011 completo
S1…1011
S2…1011
S3…1011

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:

Tabla del detector de 1011 con solapamiento (máquina de Mealy; y es la salida de la transición).
Estadox = 0x = 1Por qué
S0S0 / 0S1 / 0Un 1 comienza el patrón; un 0 no ayuda
S1S2 / 0S1 / 0«10» avanza; «11» deja solo el último 1 como prefijo
S2S0 / 0S3 / 0«101» avanza; «100» no coincide con nada
S3S2 / 0S1 / 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:

Mejor asignación de estados y tabla en binario (Q1 Q0 = código del estado).
EstadoCódigo Q1 Q0xPróximo Q1⁺ Q0⁺y
S0000000
S000111 (S1)0
S111001 (S2)0
S111111 (S1)0
S201000 (S0)0
S201110 (S3)0
S310001 (S2)0
S310111 (S1)1

Minimizando (los cuatro estados usan las cuatro combinaciones: no hay estados sin usar ni «no importa»):

D1 = x   |   D0 = Q1 + ~Q0 · x   |   y = Q1 · ~Q0 · x

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.

Conexiones del montaje 1. U1 = 74HC74; U2 = 74HC04; U3 = 74HC08; U4 = 74HC32.
ConexiónFunción
x → U1 pin 2 (1D)D1 = x
U1 pin 5 (1Q) = Q1; pin 9 (2Q) = Q0Bits 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 6y = 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 11Un reloj para los dos flip-flops
U1 pines 1 y 13 (CLR̅) ← red de reset; pines 4 y 10 (PRE̅) a +5 VEstado inicial S0 = 00
Alimentación y 100 nF en cada chip; entradas libres de U2, U3 y U4 a masaSin entradas al aire

Pruebas

Secuencia de prueba con la entrada 1 0 1 1 0 1 1 (dos apariciones solapadas del patrón).
PulsoxEstado antesEstado despuésy durante el ciclo
11S0 (00)S1 (11)0
20S1 (11)S2 (01)0
31S2 (01)S3 (10)0
41S3 (10)S1 (11)1
50S1 (11)S2 (01)0
61S2 (01)S3 (10)0
71S3 (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.

Cambios respecto del montaje 1. U5 = segundo 74HC74.
ConexiónFunció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 VArranca en 0
U5 pines 10, 11, 12 y 13Segundo 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):

y = Q3 · ~Q2 · Q1 · Q0
Conexiones del montaje 3. U6 = 74HC164; U2 = 74HC04; U3 = 74HC08 (otras tres compuertas).
ConexiónFunción
U6 pin 14 / pin 7+5 V / masa, con 100 nF entre ambos
x → U6 pin 1 (DSA); pin 2 (DSB) a +5 VEntrada serie: DSA · DSB = x
Reloj manual → U6 pin 8 (CP)Desplaza en cada flanco
U6 pin 9 (MR̅) ← red de resetPone el registro en 0000 al encender
U6 pin 5 (Q2) → inversor → ~Q2El segundo bit más viejo debe ser 0
Q1 (pin 4) y Q0 (pin 3) → AND → aa = Q1 · Q0
Q3 (pin 6) y ~Q2 → AND → bb = Q3 · ~Q2
a y b → AND → yventana → LEDy = 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.

De síntoma a causa en los montajes de este tema.
SíntomaCausa probableCó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 11Falta una condición en la ecuación de y, o la compuerta de y no recibe todas las señalesMedir 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 S2Recorrer 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 noEstado inicial indefinido (falta el reset) o rebotes en el reloj manualReiniciar y repetir; usar el reloj con latch
y parpadea al mover x con el reloj quietoSalida de Mealy: es el comportamiento esperadoUsar la versión con salida registrada (montaje 2)
La versión registrada se retrasa dos ciclosSe registró la salida dos veces o se usó el reloj invertidoUn solo flip-flop de salida con el mismo reloj
La ventana detecta apenas se enciendeMR̅ del 74HC164 sin conectar: arranca con basuraMedir el pin 9 al encender: debe estar en bajo unos milisegundos
La ventana no detecta nuncaOrden 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

  1. La tabla primero: escribirla y recorrerla con 3 o 4 secuencias antes de tocar el protoboard.
  2. El reloj manual y los flip-flops con las entradas fijas, antes de la lógica.
  3. La lógica de próximo estado y los LED de los dos bits de estado, probando la secuencia de la sección 4.
  4. La salida y al final, comprobándola con los casos de solapamiento.
  5. 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

Detector de secuencia simulado Arriba, los estados de la máquina con el estado actual resaltado, o los bits de la ventana de desplazamiento. Abajo, los últimos bits de la entrada con un borde donde termina el patrón y un triángulo donde la salida vale uno.
Estados de la máquina4
Flip-flops necesarios2
Estado o ventana actualS0
Último bit recibido—
Salida y en el último ciclo—
Ciclos con y = 10
Ocurrencias reales del patrón0
Bits aplicados0

Patrón 1011, Mealy con solapamiento. Enviá bits o la secuencia de ejemplo.

Últimos 8 bits aplicados.
N.ºxAntesDespuésy en el ciclo
Sin bits aplicados.

Recorrido guiado

  1. 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.
  2. Enviá la secuencia de ejemplo 0010110110011011: la salida vale 1 en tres ciclos (posiciones 5, 8 y 15) y coincide con las «ocurrencias reales».
  3. 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.
  4. 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.
  5. 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.
  6. 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).
  7. 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.
  8. 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.