← Índice del curso

Circuitos Secuenciales y Memoria · Tema 34

Proyecto: semáforo con máquina de estados

Es el primer proyecto integrador del curso y reúne casi todo lo visto: una base de tiempo con cristal y divisores, una máquina de estados con salidas de Moore, un contador con comparador para medir cada fase, una ROM que guarda la tabla de control, una entrada asíncrona (el botón del peatón) con antirrebote y sincronizador, y un diseño pensado para fallar de forma segura. Se trabaja como en un proyecto de verdad: se escriben los requisitos, se divide en bloques, se especifica cada uno, se verifica en código y recién entonces se arma, por etapas.

01 · Requisitos

Requisitos

Un cruce peatonal en una calle principal. Los autos tienen un semáforo de tres luces (rojo, amarillo, verde) y los peatones uno de dos (rojo y verde). Por defecto, los autos tienen el verde; un peatón puede pedir cruzar con un botón. Antes de escribir un solo circuito conviene escribir lo que el sistema debe hacer, con números:

Requisitos del semáforo.
N.ºRequisitoValor
R1Los autos tienen el verde mientras nadie pide cruzar, y al menos un tiempo mínimo cada vez que lo recuperan10 s
R2Al apretar el botón, la solicitud se memoriza; se atiende cuando termina el verde mínimo—
R3Secuencia: verde de autos → amarillo → rojo total → verde del peatón → rojo total → verde de autos10 · 3 · 2 · 8 · 2 s
R4Seguridad: el peatón nunca está en verde si los autos no están en rojo—
R5Arranque en un estado seguro (todo rojo), y salida a ese estado desde cualquier estado no previsto—
R6El botón puede apretarse en cualquier momento, con rebotes; la FSM solo ve una señal sincronizada—
R7Base de tiempo exacta: 1 Hz derivado de un cristal32 768 Hz ÷ 215
R8Un botón apretado durante el verde del peatón se atiende en el ciclo siguiente—

Con los requisitos numerados, cada decisión de diseño y cada prueba se puede justificar con «esto cumple R4». También permite detectar contradicciones temprano: por ejemplo, R2 y R1 podrían chocar (¿qué pasa si el botón se aprieta en el segundo 3 del verde?), y la respuesta ya está escrita: se espera al mínimo.

02 · Arquitectura

Arquitectura en bloques

Bloques del semáforo Un cristal y un divisor producen un reloj de 1 hertz que llega al registro de estado y al contador. La ROM de control recibe el estado, la señal FIN del comparador y la solicitud sincronizada, y entrega el próximo estado. Otra ROM entrega las luces y la duración de cada estado. El botón del peatón pasa por un antirrebote, un latch y un sincronizador de dos flip-flops. Cristal 32 768 Hz+ divisor ÷ 2^15 Botón del peatón+ antirrebote Latch + sincronizador(2 flip-flops) ROM de controlpróximo estado(estado, FIN, PEDIDO) ROM de faseluces y duración Contador + comparador(FIN = cuenta = duración − 1) Registrode estado Luces(LED) 1 HzPEDIDOFINestado
Bloques, qué hacen y de qué tema vienen.
BloqueFunciónSe estudió en
Base de tiempoCristal de 32 768 Hz y divisor por 215: reloj de 1 HzTemas 17 y 33
Registro de estadoTres flip-flops D con reloj común y resetTemas 8, 13 y 24
ROM de controlTabla de próximo estado: 32 palabras, dirección = {estado, FIN, PEDIDO}Temas 22 a 24 y 28
ROM de fasePara cada estado, la combinación de luces y la duración en segundosTema 28
Contador y comparadorCuenta los segundos de la fase y avisa con FIN cuando se cumplió su duraciónTemas 18 y 19
Solicitud peatonalAntirrebote, latch que memoriza y sincronizador de dos flip-flopsTemas 7, 11 y 20

La decisión central es separar qué estado sigue (la ROM de control) de cuánto dura cada estado (la ROM de fase y el contador). Cambiar un tiempo de la secuencia es cambiar un número en una tabla, sin tocar un solo cable; agregar un estado (por ejemplo, un verde parpadeante) es agregar una fila. El hardware es genérico y los datos son la especificación.

03 · La máquina de estados

La máquina de estados

Es una máquina de Moore (tema 23): las luces dependen solo del estado. Cinco estados forman un ciclo, y el único punto de decisión es el verde de autos:

Estados, luces y duración (la salida depende solo del estado).
EstadoNombreAutosPeatónDuraciónSigue
0AUTOS_VERDEVerdeRojo10 s (mínimo)1, si cumplió el mínimo y hay solicitud
1AUTOS_AMARILLOAmarilloRojo3 s2
2TODO_ROJO_1RojoRojo2 s3
3PEATON_VERDERojoVerde8 s4
4TODO_ROJO_2RojoRojo2 s0

Hay una entrada que importa más que el tiempo: la solicitud. El estado 0 es el único que puede durar indefinidamente; los demás duran exactamente lo que dice la tabla. Con las entradas FIN (la fase cumplió su duración) y PEDIDO (hay una solicitud sincronizada), la tabla de próximo estado es:

Tabla de próximo estado (la ROM de control).
EstadoFIN = 0FIN = 1, PEDIDO = 0FIN = 1, PEDIDO = 1
0 AUTOS_VERDE001
1 AUTOS_AMARILLO122
2 TODO_ROJO_1233
3 PEATON_VERDE344
4 TODO_ROJO_2400
5, 6, 7 (no usados)222

Los estados 5, 6 y 7 no existen en el diseño, pero 3 bits permiten esas combinaciones: ante ruido o un arranque defectuoso, la tabla manda a todos al estado 2 (todo rojo), que es el estado seguro. Es el tema de los estados no usados del tema 24, aplicado donde importa: un semáforo en un estado desconocido no puede dar verde a nadie.

La ROM de control tiene 32 palabras de 3 bits (96 bits), y el comportamiento completo del semáforo cabe en esa tabla más la de fases. Las tres condiciones que impiden el verde simultáneo (R4) son propiedades de la tabla, no del cableado: se pueden verificar con un programa antes de grabarla (paso 2 del código).

04 · El tiempo

El tiempo: reloj y temporizador

Reloj de 1 Hz

Un cristal de 32 768 Hz y un 74HC4060 (oscilador y divisor de 14 etapas) entregan 2 Hz en su salida Q14; un flip-flop T más (tema 9) da 1 Hz con 50 % de ciclo de trabajo. Como el cristal tiene una precisión del orden de 20 ppm, el reloj se adelanta o atrasa menos de dos segundos por día (tema 33). Todos los flip-flops del sistema comparten ese reloj: es un sistema totalmente síncrono de 1 Hz.

Temporizador: contador y comparador

Cada estado tiene una duración. En vez de armar un temporizador distinto para cada fase, se usa uno solo:

  • Un contador ascendente de 4 bits (74HC163, de borrado síncrono) cuenta segundos.
  • La ROM de fase entrega, según el estado actual, un número N = duración − 1.
  • Un comparador de 4 bits (74HC85) vale 1 cuando la cuenta es igual a N: esa es la señal FIN.
  • El contador se borra en el flanco en que cambia el estado (empieza de nuevo para la fase siguiente) y se detiene cuando FIN vale 1 (su habilitación es ~FIN): así, en el estado 0, que puede esperar mucho, la cuenta no da la vuelta y FIN no se pierde.
Cómo se mide cada fase (la cuenta empieza en 0 al entrar al estado).
EstadoDuraciónN = duración − 1FIN vale 1 cuando…Cambia de estado en…
0 AUTOS_VERDE10 s9cuenta = 9el flanco siguiente, si hay solicitud
1 AUTOS_AMARILLO3 s2cuenta = 2el flanco siguiente
2 TODO_ROJO_12 s1cuenta = 1el flanco siguiente
3 PEATON_VERDE8 s7cuenta = 7el flanco siguiente
4 TODO_ROJO_22 s1cuenta = 1el flanco siguiente

Con 4 bits, N llega a 15: la duración máxima de una fase es de 16 s. Para fases más largas (o de varios minutos) se agregan bits al contador y al comparador, o se cuenta a una frecuencia más baja. Este diseño es la versión de hardware del monoestable digital del tema 32: un contador, un comparador y una duración guardada en una tabla.

05 · La solicitud peatonal

La solicitud peatonal

El botón es la única entrada asíncrona del sistema y es mecánica, con rebotes. Hay que resolver tres problemas, uno por bloque:

De un botón mecánico a una entrada digital segura.
ProblemaSoluciónTema
Rebotes: varios flancos por pulsaciónFiltro RC y inversor Schmitt (74HC14)20
El pulso es breve y la FSM mira cada segundo: puede no verloUn latch (flip-flop con preset asíncrono) memoriza la solicitud hasta que se la atiende5 y 10
La solicitud cambia en cualquier instante, quizás cerca del flanco del relojUn sincronizador de dos flip-flops en serie, con el reloj de 1 Hz11

El latch es un flip-flop D con la entrada D en 0 y el preset (PRE̅) conectado a la salida del antirrebote: al apretar, Q sube al instante y queda en 1. Su clear (CLR̅) lo maneja la FSM: mientras el estado es TODO_ROJO_1 (estado 2) se borra la solicitud, porque en ese momento ya se va a atender. Una solicitud que llega durante el verde del peatón o el rojo siguiente no se borra: queda memorizada y se atiende en el ciclo siguiente (R8). Una que llega durante TODO_ROJO_1 se pierde, pero el peatón ya iba a recibir el verde.

La salida del latch pasa por dos flip-flops en cadena (SINC 1 y SINC 2); la FSM usa la salida del segundo. La cadena agrega retardo: entre 2 y 3 segundos desde que se aprieta hasta que la FSM puede decidir con esa solicitud. Es el precio de una entrada limpia. Además, como el borrado del latch tarda en propagarse por la cadena, los estados 2, 3 y 4 deben durar entre los tres lo suficiente (al menos dos flancos) como para vaciarla antes de volver al estado 0 con una solicitud vieja; con las duraciones elegidas sobran.

06 · Armado

Armado y pruebas por etapas

Lista de componentes.
Cant.ComponenteFunción
1 + 1Cristal de 32 768 Hz y 74HC4060Oscilador y divisor (Q14 = 2 Hz)
174HC74Flip-flop T (÷2) para llegar a 1 Hz y flip-flops de latch y sincronizador
174HC175 (cuatro flip-flops D con reset)Registro de estado de 3 bits
1 + 174HC163 y 74HC85Contador de 4 bits con borrado síncrono y comparador de 4 bits
1ROM pequeña (por ejemplo, una 28C16 o una porción de una 27C64)Tabla de próximo estado y de fase
174HC14Antirrebote del botón
5 + 5LED (rojo, amarillo, verde / rojo, verde) y resistencias de 330 Ω a 1 kΩLuces de autos y peatón
1 + 1Pulsador y red de reset de encendidoBotón del peatón y arranque definido (tema 10)
varios100 nF por chip, protoboard y fuente de 5 VAlimentación

Etapas

  1. Reloj de 1 Hz. Armar el cristal, el 4060 y el flip-flop T. Un LED conectado a la salida debe parpadear una vez por segundo. Medir con un cronómetro durante un minuto: 60 destellos.
  2. Registro de estado y ROM de control, con los pulsadores de FIN y PEDIDO a mano. Reloj manual (tema 7). Comprobar la tabla de próximo estado recorriendo las entradas y verificando el estado en LED.
  3. Luces. Conectar la ROM de fase a los LED. Recorrer los cinco estados a mano y verificar la combinación de luces de cada uno. Comprobar el R4: en ninguno el peatón está en verde con los autos en verde o amarillo.
  4. Contador y comparador. Con el reloj de 1 Hz, verificar que FIN sube exactamente a los N segundos de entrar en cada estado. Cronometrar cada fase.
  5. Botón. Agregar el antirrebote, el latch y el sincronizador. Apretar con rebotes provocados (varias pulsaciones rápidas) y comprobar que sale una sola solicitud.
  6. Pruebas de seguridad. Forzar un estado no usado (con un cable a un bit del registro): debe pasar a todo rojo. Apagar y encender 10 veces: debe arrancar siempre en el estado seguro.
  7. Prueba de aceptación. Recorrer los requisitos R1 a R8, uno por uno, con un cronómetro y anotando el resultado.

07 · Seguridad

Seguridad y arranque

Un semáforo es un sistema donde fallar mal tiene consecuencias. No alcanza con que funcione: hay que pensar cómo falla. Algunas ideas de diseño, todas visibles en este proyecto:

Defensas del diseño.
RiesgoDefensaDónde está
Verde simultáneo para autos y peatónLa tabla de fases no tiene ese estado; un programa verifica esa propiedad en cada filaROM de fase; paso 2 del código
Cambio directo de verde de autos a verde del peatónSiempre pasan por amarillo y rojo totalROM de control (estados 1 y 2)
Estado no previsto (ruido, arranque)Los estados 5, 6 y 7 van a TODO_ROJO_1ROM de control
Arranque en un estado cualquieraReset de encendido a un código seguro (TODO_ROJO_1 = 000)74HC175 y red RC
Entrada asíncronaAntirrebote, latch y sincronizadorBloque de la solicitud
Contenido de la ROM alteradoChecksum o CRC al arrancar (tema 28)Verificación
Fase eterna por contador que no llega a FINEl contador se detiene en N y FIN no se pierde; los estados 1 a 4 avanzan solosContador y comparador
Se pierde el relojUn monoestable con rearme (tema 32) vigila el reloj y fuerza el parpadeo del rojo si deja de llegarMejora opcional

Los controladores reales añaden un monitor de conflictos: un circuito independiente que mira las luces de salida, y si detecta dos verdes en direcciones incompatibles, desconecta el controlador y pone todo en rojo intermitente. La idea es que el sistema que verifica sea distinto del que decide, para que un error común no pase inadvertido. En este proyecto, la verificación en código cumple ese papel antes de armar.

08 · El proyecto en código

Simular, verificar, sincronizar y comparar

Los bloques simulan el comportamiento del semáforo, lo verifican con miles de segundos de botones al azar, estudian la sincronización de la solicitud y reconstruyen el hardware (ROM, contador y comparador) para comprobar que da lo mismo que el modelo.

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 modelo de comportamiento

NOMBRES = ["AUTOS_VERDE", "AUTOS_AMARILLO", "TODO_ROJO_1", "PEATON_VERDE", "TODO_ROJO_2"]
LUCES = {0: ("V", "R"), 1: ("A", "R"), 2: ("R", "R"), 3: ("R", "V"), 4: ("R", "R")}      # (autos, peatón)
DURACION = {0: 10, 1: 3, 2: 2, 3: 8, 4: 2}                                               # segundos (ticks de 1 Hz)


class Semaforo:
    def __init__(self):
        self.estado, self.en_estado = 0, 0                    # ticks transcurridos en el estado actual
        self.pedido = False                                   # solicitud peatonal memorizada

    def tick(self):
        # la decisión usa los valores de ANTES del flanco (todos se actualizan juntos)
        fin = self.en_estado + 1 >= DURACION[self.estado]
        pedido_visto = self.pedido
        if self.estado == 2:
            self.pedido = False                               # la solicitud se atiende al pasar por TODO_ROJO_1
        if self.estado == 0:
            avanza = fin and pedido_visto
        else:
            avanza = fin
        if avanza:
            self.estado = (self.estado + 1) % 5
            self.en_estado = 0
        else:
            self.en_estado = min(self.en_estado + 1, 255)


s = Semaforo()
presiones = {14}
print("t(s)  estado            autos  peatón  pedido")
anterior = None
for t in range(0, 46):
    if t in presiones:
        s.pedido = True
    if t == 0 or s.estado != anterior:
        print(f"{t:4d}  {NOMBRES[s.estado]:16s}  {LUCES[s.estado][0]:^5s}  {LUCES[s.estado][1]:^6s}  {'sí' if s.pedido else 'no'}")
    anterior = s.estado
    s.tick()
print()
print("el botón se apretó en t = 14 s: el verde de autos ya había cumplido su mínimo de 10 s, así que la fase peatonal empezó casi de inmediato.")

Sin solicitud, el semáforo se queda en AUTOS_VERDE indefinidamente. Con el botón apretado en t = 14 s, el verde de autos (que ya pasó su mínimo) cede en t = 15 s: amarillo 3 s (hasta t = 18), todo rojo 2 s (hasta t = 20), verde del peatón 8 s (hasta t = 28), todo rojo 2 s (hasta t = 30) y vuelta al verde de autos en t = 30. Un ciclo completo con solicitud son 25 segundos. La solicitud se borró al pasar por TODO_ROJO_1.

Paso 2: verificación con miles de segundos

import random

DURACION = {0: 10, 1: 3, 2: 2, 3: 8, 4: 2}


class Semaforo:
    def __init__(self):
        self.estado, self.en_estado, self.pedido = 0, 0, False

    def tick(self):
        fin = self.en_estado + 1 >= DURACION[self.estado]
        visto = self.pedido
        if self.estado == 2:
            self.pedido = False
        avanza = (fin and visto) if self.estado == 0 else fin
        if avanza:
            self.estado, self.en_estado = (self.estado + 1) % 5, 0
        else:
            self.en_estado = min(self.en_estado + 1, 255)


# luces por estado: (autos, peatón). Regla de seguridad: el peatón solo está en verde con los autos en rojo
LUCES = {0: ("V", "R"), 1: ("A", "R"), 2: ("R", "R"), 3: ("R", "V"), 4: ("R", "R")}

rnd = random.Random(34)
s = Semaforo()
violaciones, atendidas, esperas = 0, 0, []
t_press = None
transiciones = {}
for t in range(200_000):
    if rnd.random() < 0.01:                                          # alguien aprieta el botón
        if s.estado != 2:
            s.pedido = True
            if t_press is None:
                t_press = t
    anterior = s.estado
    s.tick()
    autos, peaton = LUCES[s.estado]
    if peaton == "V" and autos != "R":
        violaciones += 1                                             # verde para el peatón con autos en verde o amarillo
    if s.estado != anterior:
        transiciones[(anterior, s.estado)] = transiciones.get((anterior, s.estado), 0) + 1
    if s.estado == 3 and anterior == 2:
        if t_press is not None:
            esperas.append(t - t_press)
            t_press = None
        atendidas += 1

print(f"{atendidas} cruces peatonales atendidos en 200 000 s; instantes con peatón en verde y autos no en rojo: {violaciones}")
print("transiciones observadas:", ", ".join(f"{a}->{b}" for (a, b) in sorted(transiciones)))
print(f"espera del peatón desde que aprieta hasta que sale el verde: mínima {min(esperas)} s, promedio {sum(esperas) / len(esperas):.1f} s, máxima {max(esperas)} s")
cota1 = DURACION[1] + DURACION[2]
cota2 = DURACION[0] + DURACION[1] + DURACION[2]
cota3 = DURACION[3] + DURACION[4] + DURACION[0] + DURACION[1] + DURACION[2]
print(f"cotas teóricas: {cota1} s si el verde mínimo ya pasó (amarillo + rojo), {cota2} s si recién empezó el verde, {cota3} s si el botón se apretó justo al empezar la fase peatonal")
print(f"el peor caso medido fue {max(esperas)} s, dentro de la cota de {cota3} s")

En 200 000 segundos con botones al azar, el semáforo atendió 1837 cruces y no hubo ni un solo instante con el peatón en verde y los autos en verde o amarillo (R4). Las únicas transiciones son 0→1→2→3→4→0: el ciclo nunca se saltea un estado. La espera del peatón va de 5 s (cuando el verde mínimo ya pasó: amarillo + rojo) hasta 24 s (cuando justo acaba de empezar la fase peatonal y la nueva solicitud debe esperar un ciclo entero); el promedio es de 7 s. El peor caso teórico es de 25 s.

Paso 3: la solicitud y el sincronizador

def simular(instantes_boton, ticks=12):
    """Latch de solicitud + sincronizador de dos flip-flops, con reloj de 1 Hz.
    El botón puede caer en cualquier instante (en segundos); el latch lo memoriza al instante;
    en cada flanco entero (1, 2, 3 ...) la cadena se desplaza. Devuelve el flanco en que la FSM ve la solicitud."""
    p, s1, s2 = False, False, False
    for t in range(1, ticks + 1):
        # antes del flanco t: el botón puede haberse apretado en el intervalo (t-1, t]
        if any(t - 1 < x <= t for x in instantes_boton):
            p = True
        visto_por_la_fsm = s2                       # lo que la FSM ve DURANTE este flanco (valor previo)
        s2, s1 = s1, p                              # la cadena se desplaza en el flanco
        if visto_por_la_fsm:
            return t
    return None


for x in (2.1, 2.5, 2.95):
    t = simular([x])
    print(f"botón apretado en t = {x} s -> la FSM puede decidir con esa solicitud en el flanco {t} (espera {t - x:.2f} s)")

print()
print("10 cierres del contacto dentro de un mismo segundo (rebotes):")
t1 = simular([2.1])
t10 = simular([2.1 + 0.005 * k for k in range(10)])
print(f"   con un solo cierre la FSM decide en el flanco {t1}; con 10 cierres, en el flanco {t10}: el latch guarda UNA solicitud, no diez")

print()
print("la cadena de dos flip-flops agrega entre 2 y 3 s de retardo en el peor caso; a cambio, la FSM nunca ve una entrada que cambie cerca del flanco")
print("(sin sincronizador, un botón apretado justo en el flanco puede dejar el estado en un valor intermedio: metastabilidad, tema 11)")

Un botón apretado en cualquier instante del segundo 2 (2,1; 2,5 o 2,95) es visto por la FSM en el mismo flanco 5: la espera varía entre 2,05 y 2,9 s según cuánto después del flanco 2 cayó la pulsación. Diez cierres de contacto dentro del mismo segundo producen la misma solicitud única, porque el latch memoriza un bit. El sincronizador es el responsable de que la FSM jamás vea la entrada cambiar justo en el flanco.

Paso 4: la versión de hardware (ROM, contador y comparador)

import random

DURACION = {0: 10, 1: 3, 2: 2, 3: 8, 4: 2}
N = {e: d - 1 for e, d in DURACION.items()}                           # el comparador usa "ticks menos 1"


def construir_rom():
    """ROM de próximo estado: dirección = {estado(3 bits), FIN, PEDIDO}."""
    rom = {}
    for estado in range(8):
        for fin in (0, 1):
            for pedido in (0, 1):
                if estado == 0:
                    prox = 1 if (fin and pedido) else 0
                elif estado in (1, 2, 3):
                    prox = estado + 1 if fin else estado
                elif estado == 4:
                    prox = 0 if fin else 4
                else:
                    prox = 2                                         # estados no usados: ir al TODO_ROJO_1 (seguro)
                rom[(estado << 2) | (fin << 1) | pedido] = prox
    return rom


ROM = construir_rom()
print("ROM de próximo estado (32 palabras de 3 bits), dirección = {estado, FIN, PEDIDO}:")
for estado in range(5):
    fila = [ROM[(estado << 2) | (fin << 1) | pedido] for fin in (0, 1) for pedido in (0, 1)]
    print(f"  estado {estado}: FIN,PEDIDO = 00 -> {fila[0]}, 01 -> {fila[1]}, 10 -> {fila[2]}, 11 -> {fila[3]}")
print("  estados 5, 6 y 7 (no usados): siempre -> 2 (todo rojo)")
print(f"  total: {len(ROM)} palabras de 3 bits = {len(ROM) * 3} bits")


class Hardware:
    """Registro de estado + contador ascendente + comparador + ROM, todo en el mismo flanco."""

    def __init__(self):
        self.estado, self.cuenta = 0, 0
        self.p, self.s1, self.s2 = False, False, False

    def tick(self):
        fin = int(self.cuenta == N.get(self.estado, 0))
        prox = ROM[(self.estado << 2) | (fin << 1) | int(self.s2)]
        # el contador se borra al cambiar de estado y se detiene cuando FIN = 1
        if prox != self.estado:
            nueva_cuenta = 0
        else:
            nueva_cuenta = self.cuenta if fin else self.cuenta + 1
        borrar_pedido = self.estado == 2
        self.s2, self.s1 = self.s1, self.p
        if borrar_pedido:
            self.p = False
        self.estado, self.cuenta = prox, nueva_cuenta


rnd = random.Random(34)
h = Hardware()
estados = []
for t in range(20_000):
    if rnd.random() < 0.02 and h.estado != 2:
        h.p = True
    h.tick()
    estados.append(h.estado)
visitas = {e: estados.count(e) for e in range(8)}
print()
print("20 000 s con el botón al azar: segundos pasados en cada estado:", visitas)
print("ningún segundo en los estados 5, 6 o 7:", all(visitas[e] == 0 for e in (5, 6, 7)))

# fuerzo un estado no usado: debe caer en TODO_ROJO_1
h2 = Hardware()
h2.estado = 6
h2.tick()
print(f"si por ruido el registro queda en 6: tras un flanco pasa a {h2.estado} (todo rojo)")

# la duración de cada fase medida en segundos (con una solicitud ya presente en el sincronizador)
h3 = Hardware()
h3.p = True
h3.s1 = h3.s2 = True
dur, actual, cuenta = {}, h3.estado, 0
for t in range(60):
    h3.tick()
    cuenta += 1
    if h3.estado != actual:
        dur.setdefault(actual, cuenta)
        actual, cuenta = h3.estado, 0
print("duración medida de cada fase (s):", dur, "| esperada:", DURACION)

La ROM de control completa son 32 palabras de 3 bits (96 bits) y cada fila coincide con la tabla de la sección 3. Con el botón al azar durante 20 000 segundos, el hardware nunca pasa un segundo en los estados 5, 6 o 7. Si un bit del registro se corrompe y queda en 6, un flanco después el estado es 2, todo rojo. Las duraciones medidas (10, 3, 2, 8 y 2 s) coinciden con las especificadas: el esquema «contador que se detiene en N + comparador» produce exactamente las duraciones de la tabla.

Los cuatro bloques son la forma profesional de trabajar con un diseño digital: un modelo de comportamiento, una verificación exhaustiva contra los requisitos, un estudio de la parte delicada (la entrada asíncrona) y una versión de hardware comprobada contra el modelo.

09 · Exploración interactiva

El laboratorio: el semáforo funcionando

El simulador es el hardware del proyecto: registro de estado, contador con comparador, latch de solicitud y sincronizador de dos flip-flops. A la izquierda y a la derecha se ven los semáforos de autos y de peatones; en el centro, el estado, la cuenta, las señales PEDIDO, SINC 1, SINC 2 y FIN, y debajo, una franja con los últimos 60 segundos. Podés avanzar de a un flanco, de a diez o reproducir a 4× la velocidad. Los cinco controles deslizantes cambian las duraciones: son los números de la ROM de fase.

Semáforo con cruce peatonal

Semáforo simulado Un semáforo de tres luces para autos a la izquierda, uno de dos luces para peatones a la derecha, y en el centro el estado actual, el contador, las señales de solicitud y la señal FIN. Debajo, una franja con los colores de los últimos sesenta segundos para autos y peatones.
Tiempo simulado0 s
Estado0 · AUTOS_VERDE
Contador (cuenta / N)0 / 9
FIN0
Solicitud (latch y sincronizador)—
Cruces atendidos0
Espera del peatón (última y máxima)—
Violaciones de seguridad0
Recuperaciones de estado no usado0

Autos en verde, esperando una solicitud. Apretá el botón del peatón y avanzá el tiempo.

Últimos 8 cambios de estado.
N.ºInstanteCambioMotivo
Sin cambios de estado.

Recorrido guiado

  1. Con el reloj corriendo (+10 s o reproducir) y sin apretar el botón, el semáforo se queda en AUTOS_VERDE: el contador llega a 9 de 9, FIN vale 1 y se detiene ahí. Nada cambia sin una solicitud.
  2. Apretá el botón: PEDIDO se enciende de inmediato (el latch). Avanzá de a 1 s: en cada flanco la solicitud pasa a SINC 1 y luego a SINC 2. Con FIN ya en 1, la FSM cambia al amarillo en cuanto SINC 2 vale 1, unos 2 s después.
  3. Seguí avanzando: amarillo 3 s, todo rojo 2 s, verde del peatón 8 s, todo rojo 2 s y de vuelta al verde de autos. El contador se reinicia en cada cambio y la tabla de la derecha registra cada transición.
  4. Apretá el botón durante el verde del peatón: PEDIDO queda en 1 (R8). Al volver al verde de autos, la solicitud ya está; en cuanto pasan los 10 s mínimos, comienza otro ciclo sin que nadie apriete de nuevo.
  5. Apretá el botón en TODO_ROJO_1: el simulador avisa que la solicitud se pierde (ese estado borra el latch), pero el peatón ya va a recibir el verde en esos instantes.
  6. Apretá el botón apenas vuelven los autos al verde (en los primeros segundos de AUTOS_VERDE): la espera hasta el amarillo es la del verde mínimo restante. «Espera del peatón» registra la última y la máxima (hasta 25 s con los tiempos por defecto).
  7. Presioná «Forzar un estado no usado»: el registro pasa a 5, 6 o 7 con todo rojo y, en el flanco siguiente, a TODO_ROJO_1. «Recuperaciones» sube 1 y «Violaciones» queda en 0.
  8. Cambiá las duraciones: alargá el amarillo, acortá el rojo total, etc. Las violaciones de seguridad siguen en 0 porque la regla R4 depende de la tabla de estados, no de los tiempos.
Desafío: agregar un verde parpadeante para el peatón

Para avisar que el tiempo de cruce se agota, el verde del peatón debería parpadear los últimos 3 segundos. Se agrega un estado: PEATON_PARPADEO (autos en rojo, peatón con verde intermitente) entre PEATON_VERDE y TODO_ROJO_2. Pasa de 5 a 6 estados: siguen alcanzando 3 bits (quedan 2 estados no usados que van a TODO_ROJO_1). PEATON_VERDE dura ahora 5 s y el nuevo estado, 3 s. La luz parpadeante sale de combinar el estado con el reloj de 1 Hz (o con uno de 2 Hz), una compuerta AND: una salida de Mealy (tema 23) que conviene registrar para que no tenga glitches.

10 · Comprensión

Confusiones frecuentes

«La FSM mira el botón directamente»

Mira la salida del sincronizador. El botón es asíncrono y con rebotes; conectarlo directo a la lógica sincrónica puede provocar estados metaestables o múltiples solicitudes.

«Si el botón se aprieta durante la fase peatonal, se pierde»

Se conserva en el latch y se atiende en el ciclo siguiente (R8). Solo se pierde si se aprieta durante TODO_ROJO_1, cuando el peatón ya está a punto de recibir el verde.

«El contador debe dar la vuelta para que el verde de autos se acabe»

Al revés: el contador se detiene en N para que FIN no se pierda. Si diera la vuelta, FIN dejaría de valer 1 y una solicitud tardía esperaría hasta la vuelta siguiente.

«Cambiar la duración de una fase obliga a rediseñar el circuito»

Solo cambia un número en la ROM de fase. El circuito es genérico: la especificación vive en los datos.

«Los estados 5, 6 y 7 no importan porque nunca se usan»

Importan porque el ruido o un arranque defectuoso pueden llevar el registro allí. Llevarlos al estado de todo rojo es parte del diseño seguro.

«El semáforo es seguro porque funciona en las pruebas»

La seguridad se demuestra verificando las propiedades en toda la tabla y simulando miles de segundos con entradas al azar, no con unos pocos casos. Y se completa con la verificación independiente en un sistema real.

«Más retardo del sincronizador no importa»

Entre 2 y 3 segundos en este diseño: aceptable para un botón de peatón. En un sistema más rápido, el retardo de la sincronización se paga en latencia y hay que contarlo.

«El verde mínimo es una pérdida de tiempo para el peatón»

Es una decisión de tráfico: garantiza que los autos circulen un tiempo razonable antes de ser interrumpidos. Es un parámetro, y por eso está en una tabla.

11 · Práctica guiada

Predecí antes de simular

1. Duración del ciclo

Con las duraciones por defecto, ¿cuánto dura un ciclo completo desde que los autos salen del verde hasta que lo recuperan? ¿Y desde que se aprieta el botón (con el verde mínimo ya cumplido) hasta el verde del peatón?

Ver solución

Del amarillo al regreso del verde: 3 + 2 + 8 + 2 = 15 s (más el verde mínimo de 10 s: ciclo completo de 25 s). Desde el botón hasta el verde del peatón: amarillo + rojo = 3 + 2 = 5 s, más 2 a 3 s del sincronizador al decidir.

2. Un tiempo más largo

Se quiere un verde del peatón de 12 s. ¿Qué hay que cambiar en el circuito? ¿Cabe en el contador de 4 bits?

Ver solución

Solo el valor de N para ese estado en la ROM de fase: N = 11. Cabe, porque 4 bits llegan hasta N = 15 (fases de hasta 16 s). El resto del hardware no cambia.

3. Ancho del contador

Una fase debe durar 40 s. ¿Cuántos bits necesita el contador y el comparador?

Ver solución

N = 39. Como 25 = 32 < 40 ≤ 64 = 26, hacen falta 6 bits en el contador y en el comparador (dos 74HC163 y dos 74HC85 en cascada, o una versión de más bits).

4. El peor caso

Con los tiempos por defecto, ¿cuál es la mayor espera posible de un peatón y en qué circunstancia ocurre?

Ver solución razonada

Ocurre cuando el botón se aprieta justo después de empezar el verde del peatón del ciclo anterior (la solicitud se memoriza para el siguiente): 8 (resto del verde del peatón) + 2 (rojo) + 10 (verde mínimo de autos) + 3 (amarillo) + 2 (rojo) = 25 s hasta el siguiente verde del peatón. En la simulación de 200 000 s el máximo observado fue 24 s.

5. Un estado más

Se agrega un estado PEATON_PARPADEO. ¿Cuántos estados hay, cuántos bits de estado hacen falta y cuántos estados no usados quedan?

Ver solución

6 estados: 3 bits alcanzan (23 = 8). Quedan 2 estados no usados (6 y 7), que deben ir al estado seguro. La ROM de control pasa a tener una fila más con datos.

6. Diagnóstico

Después de un cruce, el semáforo vuelve a repetir la fase peatonal una y otra vez aunque nadie apriete el botón. ¿Qué está mal?

Ver solución razonada

La solicitud no se borra: probablemente el clear (CLR̅) del latch no llega desde la señal «estado = TODO_ROJO_1», o el preset del latch está permanentemente activo (el botón o el antirrebote quedó en bajo). El latch sigue en 1, el sincronizador sigue en 1 y, apenas se cumple el verde mínimo, la FSM inicia otro ciclo. Se mide PEDIDO con el botón suelto: debe estar en 0.

12 · Síntesis

Ideas para recordar

  • Un proyecto empieza por requisitos numerados y medibles; cada decisión de diseño y cada prueba se justifica con ellos.
  • La arquitectura separa qué estado sigue (ROM de control), qué luces hay y cuánto dura cada fase (ROM de fase) y cómo se mide el tiempo (contador y comparador). El hardware es genérico; la especificación vive en tablas.
  • La máquina es de Moore con cinco estados; el verde de autos es el único que espera: avanza solo con FIN y PEDIDO. Los estados 5, 6 y 7 van a TODO_ROJO_1.
  • El contador sube desde 0 al entrar al estado, se detiene en N = duración − 1 y FIN = (cuenta = N). Con 4 bits, las fases llegan a 16 s.
  • La solicitud peatonal pasa por antirrebote, latch (que la memoriza hasta atenderla) y sincronizador de dos flip-flops (2 a 3 s de retardo); el latch se borra en TODO_ROJO_1.
  • La seguridad es una propiedad de las tablas (nunca verde simultáneo, siempre amarillo y rojo intermedio) y se verifica con código sobre miles de segundos con botones al azar.
  • El arranque y los errores son parte del diseño: reset a un estado seguro, estados no usados que llevan a rojo, verificación de la ROM y, en sistemas reales, un monitor de conflictos independiente.
  • Se arma por etapas (reloj, estado, luces, contador, botón, seguridad, aceptación) y se compara cada una con el modelo.

¿Listo para el tema 35?

Antes de avanzar, explicá de memoria por qué el contador se detiene en N, qué función cumple cada uno de los tres bloques de la solicitud peatonal y cómo se demuestra que el peatón nunca recibe verde con los autos en verde. Si podés justificar las tres respuestas, ya podés encarar el segundo proyecto, que lleva las luces a una memoria.

En el próximo tema armaremos el secuenciador con memoria: un contador que recorre direcciones y una memoria que guarda los patrones de luces, de modo que cambiar la secuencia sea cambiar los datos.