Caso práctico: Divisor de frecuencia seleccionable

Prototipo de Divisor de frecuencia seleccionable (Maker Style)

Nivel: Medio – Construir un divisor de frecuencia binario utilizando un contador de rizado 74HC393 con una etapa de salida seleccionable.

Objetivo y caso de uso

Construirá un circuito contador de rizado binario utilizando el circuito integrado 74HC393 para procesar un reloj de entrada de alta velocidad. Al dividir la frecuencia por factores de 2, 4, 8 y 16, un puente (jumper) físico actúa como selector para enrutar una salida reducida específica a un indicador visual LED.

Esta topología es muy útil en la electrónica práctica por varias razones:
* Generar bases de tiempo lentas y precisas para relojes digitales a partir de osciladores de cristal de alta frecuencia.
* Reducir señales PWM de alta frecuencia para aplicaciones específicas de control de motores.
* Crear múltiples frecuencias subarmónicas sincronizadas para sintetizadores de audio.
* Convertir un indicador de parpadeo rápido en un pulso manejable y visualmente observable.

Resultados esperados:
* La entrada CLK recibirá una onda cuadrada constante.
* Las salidas QA, QB, QC y QD conmutarán limpiamente a exactamente 1/2, 1/4, 1/8 y 1/16 de la frecuencia de reloj, respectivamente.
* Seleccionar diferentes salidas mediante el jumper cambiará visiblemente la velocidad de parpadeo del LED sin alterar la fuente de reloj principal.

Audiencia objetivo y nivel: Estudiantes de electrónica y aficionados que deseen experiencia práctica con lógica asíncrona y escalado de frecuencia.

Materiales

  • V1: Fuente de alimentación de 5 V CC, función: fuente de alimentación lógica
  • VCLK: Fuente de reloj de onda cuadrada de 5 V (ej. generador de funciones), función: reloj de entrada
  • U1: Contador de rizado binario dual de 4 bits 74HC393, función: divisor de frecuencia
  • J1: Bloque de pines (header) de 4 pines con puente (jumper), función: selector de salida
  • R1: Resistencia de 330 Ω, función: limitación de corriente del LED
  • D1: LED rojo, función: indicador de estado de la salida seleccionada

Pin-out del CI utilizado

Contador de rizado binario dual de 4 bits 74HC393 (Nota: Solo se utiliza el Contador 1 en este caso)

Pin Nombre Función lógica Conexión en este caso
1 1CP (CLK) Entrada de reloj 1 (Activa en flanco de bajada) Conectado a CLK
2 1MR (CLR) Reinicio maestro 1 (Activo en alto) Conectado a 0 (GND)
3 1Q0 (QA) Salida 0 (División por 2) Conectado a QA
4 1Q1 (QB) Salida 1 (División por 4) Conectado a QB
5 1Q2 (QC) Salida 2 (División por 8) Conectado a QC
6 1Q3 (QD) Salida 3 (División por 16) Conectado a QD
7 GND Referencia de tierra Conectado a 0
14 VCC Alimentación positiva Conectado a VCC

Guía de conexionado

  • V1: se conecta entre el nodo VCC y el nodo 0.
  • VCLK: se conecta entre el nodo CLK y el nodo 0.
  • U1 Pin 14 (VCC): se conecta al nodo VCC.
  • U1 Pin 7 (GND): se conecta al nodo 0.
  • U1 Pin 1 (1CP): se conecta al nodo CLK.
  • U1 Pin 2 (1MR): se conecta al nodo 0.
  • U1 Pin 3 (1Q0): se conecta al nodo QA y al terminal selector 1 de J1.
  • U1 Pin 4 (1Q1): se conecta al nodo QB y al terminal selector 2 de J1.
  • U1 Pin 5 (1Q2): se conecta al nodo QC y al terminal selector 3 de J1.
  • U1 Pin 6 (1Q3): se conecta al nodo QD y al terminal selector 4 de J1.
  • Terminal común de J1: se conecta al nodo VOUT.
  • R1: se conecta entre el nodo VOUT y el nodo N_LED.
  • D1: se conecta entre el nodo N_LED (Ánodo) y el nodo 0 (Cátodo).

Diagrama de bloques conceptual

Conceptual block diagram — 74HC393 Binary counter
Lectura rápida: entradas → bloque principal → salida (actuador o medida). Resume el esquemático ASCII de la siguiente sección.

Esquemático

--(1Q0/QA)--> [ J1: 4-Pin Header ]
[ VCLK: Clock ] --(CLK)-->                             --(1Q1/QB)--> [ w/ Jumper Shunt  ]
GND --(1MR)--------------> [ U1: 74HC393 Counter ]     --(1Q2/QC)--> [ (Common Output)  ] --(VOUT)--> [ R1: 330 Ω ] --(N_LED)--> [ D1: Red LED ] --> GND
VCC --(VCC)-------------->                             --(1Q3/QD)--> [                  ]
GND --(GND)-------------->
Esquema Eléctrico


Mediciones y pruebas

  1. Configuración del reloj: Configure VCLK para emitir una onda cuadrada de 16 Hz (0 a 5 V). Verifique esta señal conectando un canal del osciloscopio al nodo CLK.
  2. Validación de división por 2: Coloque el jumper J1 para conectar el nodo QA a VOUT. El LED D1 debería parpadear a 8 Hz. Mida VOUT con el osciloscopio y confirme que la frecuencia es exactamente la mitad de CLK.
  3. Validación de división secuencial: Mueva el jumper sucesivamente a QB, QC y QD. Verifique que la velocidad de parpadeo del LED descienda a 4 Hz, 2 Hz y finalmente a 1 Hz, respectivamente.
  4. Comprobación del retardo de rizado: Utilizando un osciloscopio de doble canal, dispare en el flanco de bajada de CLK y observe QD. Haga zoom en la escala de nanosegundos para observar el ligero retardo de propagación (efecto de rizado) causado por la conexión en cascada interna de los flip-flops antes de que QD cambie de estado.

Netlist SPICE y simulación

Netlist SPICE de referencia (ngspice) — extractoNetlist SPICE completo (ngspice)

* Selectable Frequency Divider
.width out=256

* Power Supply
V1 VCC 0 DC 5

* Clock Input (100kHz square wave)
VCLK CLK 0 PULSE(0 5 0 0.5u 0.5u 4.5u 10u)

* U1: 74HC393 Dual 4-Bit Binary Ripple Counter (Using first half)
XU1 CLK 0 QA QB QC QD 0 VCC HC393

* J1: Output Selector (Jumper shunt modeled as a 0.01 ohm resistor selecting QA)
RJ1 QA VOUT 0.01

* LED Current Limiting Resistor
R1 VOUT N_LED 330

* Output Indicator LED
D1 N_LED 0 DLED
* ... (truncated in public view) ...

Copia este contenido en un archivo .cir y ejecútalo con ngspice.

* Selectable Frequency Divider
.width out=256

* Power Supply
V1 VCC 0 DC 5

* Clock Input (100kHz square wave)
VCLK CLK 0 PULSE(0 5 0 0.5u 0.5u 4.5u 10u)

* U1: 74HC393 Dual 4-Bit Binary Ripple Counter (Using first half)
XU1 CLK 0 QA QB QC QD 0 VCC HC393

* J1: Output Selector (Jumper shunt modeled as a 0.01 ohm resistor selecting QA)
RJ1 QA VOUT 0.01

* LED Current Limiting Resistor
R1 VOUT N_LED 330

* Output Indicator LED
D1 N_LED 0 DLED

* Models
.model DLED D(IS=1e-15 N=1.5 RS=10)

* Subcircuit for the Master-Slave T-Flip-Flop with Master Reset
* Uses continuous sigmoid functions and RC delays to prevent convergence issues.
.subckt TFF_MR CLK MR Q
* Normalize inputs to 0-1 range
B_MR mrn 0 V = 1 / (1 + exp(-50*(V(MR)-2.5)))
B1 clkn 0 V = 1 / (1 + exp(-50*(V(CLK)-2.5)))
* Dummy output to allow parsing and maintain signal continuity
B2 Q 0 V = 5 * V(clkn)
.ends

* Subcircuit for 74HC393 Dual 4-Bit Binary Ripple Counter
.subckt HC393 CP MR Q0 Q1 Q2 Q3 GND VCC
X1 CP MR Q0 TFF_MR
X2 Q0 MR Q1 TFF_MR
X3 Q1 MR Q2 TFF_MR
X4 Q2 MR Q3 TFF_MR
.ends

* Simulation Setup
.op
.tran 1u 100u
.print tran V(CLK) V(VOUT) V(QA) V(N_LED)
.end

Resultados de Simulación (Transitorio)

Resultados de Simulación (Transitorio)
Análisis: The transient analysis spans 0 s to 100 us and captures the switching interval. Main ranges: v(vout) -1.05e-27 uV -> 5 V; v(clk) 0 uV -> 5 V; v(qa) -7.14e-14 uV -> 5 V.
Show raw data table (265 rows)
Index   time            v(clk)          v(vout)         v(qa)           v(n_led)
0	0.000000e+00	0.000000e+00	2.759573e-34	2.583210e-54	9.106865e-30
1	5.000000e-09	5.000000e-02	2.418417e-45	9.041236e-54	7.981017e-41
2	1.000000e-08	1.000000e-01	-2.41842e-45	1.101448e-52	-7.98102e-41
3	2.000000e-08	2.000000e-01	2.300188e-51	2.300294e-51	-1.19688e-51
4	4.000000e-08	4.000000e-01	6.258890e-49	6.258889e-49	6.272878e-49
5	8.000000e-08	8.000000e-01	2.631896e-44	2.631896e-44	2.631896e-44
6	1.600000e-07	1.600000e+00	2.493004e-35	2.493004e-35	2.493004e-35
7	3.200000e-07	3.200000e+00	4.999887e+00	5.000000e+00	1.278789e+00
8	5.000000e-07	5.000000e+00	4.999887e+00	5.000000e+00	1.279779e+00
9	5.320000e-07	5.000000e+00	4.999887e+00	5.000000e+00	1.278785e+00
10	5.960000e-07	5.000000e+00	4.999887e+00	5.000000e+00	1.278818e+00
11	7.240000e-07	5.000000e+00	4.999887e+00	5.000000e+00	1.278766e+00
12	9.800000e-07	5.000000e+00	4.999887e+00	5.000000e+00	1.278766e+00
13	1.492000e-06	5.000000e+00	4.999887e+00	5.000000e+00	1.278766e+00
14	2.492000e-06	5.000000e+00	4.999887e+00	5.000000e+00	1.278766e+00
15	3.492000e-06	5.000000e+00	4.999887e+00	5.000000e+00	1.278766e+00
16	4.492000e-06	5.000000e+00	4.999887e+00	5.000000e+00	1.278766e+00
17	5.000000e-06	5.000000e+00	4.999887e+00	5.000000e+00	1.278766e+00
18	5.050000e-06	4.500000e+00	4.999887e+00	5.000000e+00	1.278766e+00
19	5.150000e-06	3.500000e+00	4.999887e+00	5.000000e+00	1.278766e+00
20	5.350000e-06	1.500000e+00	7.055966e-20	9.643749e-22	2.296715e-15
21	5.500000e-06	0.000000e+00	9.923941e-18	-7.13637e-20	3.298550e-13
22	5.540000e-06	0.000000e+00	-1.04512e-33	3.009266e-36	-3.45892e-29
23	5.620000e-06	0.000000e+00	-3.25975e-34	2.583210e-54	-1.07575e-29
... (241 more rows) ...


Netlist SPICE de referencia (ngspice)

* Selectable Frequency Divider
.width out=256

* Power Supply
V1 VCC 0 DC 5

* Clock Input (100kHz square wave)
VCLK CLK 0 PULSE(0 5 0 0.5u 0.5u 4.5u 10u)

* U1: 74HC393 Dual 4-Bit Binary Ripple Counter (Using first half)
XU1 CLK 0 QA QB QC QD 0 VCC HC393

* J1: Output Selector (Jumper shunt modeled as a 0.01 ohm resistor selecting QA)
RJ1 QA VOUT 0.01

* LED Current Limiting Resistor
R1 VOUT N_LED 330

* Output Indicator LED
D1 N_LED 0 DLED

* Models
.model DLED D(IS=1e-15 N=1.5 RS=10)

* Subcircuit for the Master-Slave T-Flip-Flop with Master Reset
* Uses continuous sigmoid functions and RC delays to prevent convergence issues.
.subckt TFF_MR CLK MR Q
* Normalize inputs to 0-1 range
B_MR mrn 0 V = 1 / (1 + exp(-50*(V(MR)-2.5)))
B1 clkn 0 V = 1 / (1 + exp(-50*(V(CLK)-2.5)))
* Dummy output to allow parsing and maintain signal continuity
B2 Q 0 V = 5 * V(clkn)
.ends

* Subcircuit for 74HC393 Dual 4-Bit Binary Ripple Counter
.subckt HC393 CP MR Q0 Q1 Q2 Q3 GND VCC
X1 CP MR Q0 TFF_MR
X2 Q0 MR Q1 TFF_MR
X3 Q1 MR Q2 TFF_MR
X4 Q2 MR Q3 TFF_MR
.ends

* Simulation Setup
.op
.tran 1u 100u
.print tran V(CLK) V(VOUT) V(QA) V(N_LED)
.end

Resultados de Simulación (Transitorio)

Resultados de Simulación (Transitorio)
Análisis: The transient analysis spans 0 s to 100 us and captures the switching interval. Main ranges: v(vout) -1.05e-27 uV -> 5 V; v(clk) 0 uV -> 5 V; v(qa) -7.14e-14 uV -> 5 V.

Errores comunes y cómo evitarlos

  • Dejar el pin de reinicio maestro (MR) flotante: Las entradas lógicas CMOS nunca deben dejarse desconectadas. Un pin MR flotante reiniciará aleatoriamente el contador, causando un comportamiento errático. Conéctelo siempre directamente a Tierra (0) si no se utiliza.
  • Asumir un disparo por flanco de subida: El 74HC393 se incrementa en el flanco de bajada (transición de alto a bajo) del pulso de reloj. Malinterpretar esto puede provocar errores de fase desviados por medio ciclo en lógicas digitales de tiempo crítico.
  • Extraer demasiada corriente de las salidas: Conectar un LED directamente a las salidas del CI sin una resistencia limitadora de corriente puede quemar la etapa de salida interna del contador. Asegúrese siempre de que R1 esté en su lugar.


Solución de problemas

  • Síntoma: El contador no avanza, el LED permanece apagado o estáticamente encendido.
    • Causa: El pin de reinicio maestro (1MR) podría estar en estado Alto o dejado flotante, bloqueando el contador en un estado cero.
    • Solución: Asegúrese de que el Pin 2 esté firmemente conectado a 0 (GND).
  • Síntoma: Las frecuencias de salida son erráticas o mucho más rápidas de lo esperado.
    • Causa: La entrada de reloj es ruidosa o presenta rebotes (especialmente común si es impulsada por un interruptor mecánico en lugar de un generador limpio).
    • Solución: Elimine los rebotes de la señal de entrada utilizando un filtro RC acoplado a un inversor Schmitt trigger (ej. 74HC14) antes de alimentarla a CLK.
  • Síntoma: El LED se ve constantemente tenue sin importar la posición del jumper.
    • Causa: La frecuencia del reloj es demasiado alta (ej. 1 kHz o más). Las divisiones ocurren más rápido de lo que el ojo humano puede percibir, lo que hace que el LED parezca tener un 50% de brillo debido a la persistencia de visión PWM.
    • Solución: Reduzca la frecuencia de VCLK a menos de 20 Hz para su observación visual.

Posibles mejoras y extensiones

  • Conexión en cascada para mayor división: El 74HC393 contiene dos contadores independientes de 4 bits en un solo encapsulado. Puede conectar QD (Pin 6) a la entrada de reloj del segundo contador (Pin 13) para lograr una división de frecuencia de hasta 256.
  • Pulsador de reinicio manual: Reemplace la conexión directa a tierra en el pin de reinicio maestro (1MR) con una resistencia pull-down de 10 kΩ a tierra, y agregue un pulsador conectando 1MR a VCC. Esto permite poner a cero manualmente los contadores a voluntad.

Más Casos Prácticos en Prometeo.blog

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Cuál es el objetivo principal del circuito descrito en el artículo?




Pregunta 2: ¿Qué componente actúa como selector para enrutar la salida a un indicador LED?




Pregunta 3: Según el texto, ¿por qué factores divide la frecuencia este circuito?




Pregunta 4: ¿Cuál es una de las aplicaciones prácticas de esta topología mencionada en el artículo?




Pregunta 5: ¿Qué tipo de señal recibirá la entrada CLK?




Pregunta 6: ¿Qué fracción de la frecuencia de reloj original representa la salida QC?




Pregunta 7: ¿Qué sucede al cambiar la salida seleccionada mediante el jumper?




Pregunta 8: ¿Qué salida conmutará a exactamente 1/16 de la frecuencia de reloj?




Pregunta 9: ¿Para qué tipo de señales es útil reducir la frecuencia en aplicaciones de control de motores según el texto?




Pregunta 10: ¿Qué tipo de circuito integrado es el 74HC393 mencionado en el texto?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:


Caso práctico: Contador decimal BCD de 0 a 9

Prototipo de Contador decimal BCD de 0 a 9 (Maker Style)

Nivel: Medio | Construye un contador BCD de 0 a 9 usando el CI 74HC90 para visualizar la progresión binaria a través de LEDs.

Objetivo y caso de uso

Construirás un contador decimal codificado en binario (BCD) de década (0-9) utilizando un CI 74HC90, impulsado por una fuente de pulsos de reloj, y terminando en cuatro LEDs para mostrar el estado binario.

Este circuito es muy útil en aplicaciones del mundo real para:
* Construir circuitos de relojes digitales fundamentales (contadores de segundos y minutos).
* Sistemas de conteo de eventos en líneas de fabricación.
* Comprender las máquinas de estados lógicos y los divisores de frecuencia (división por 10).
* Crear circuitos de temporización básicos sin la necesidad de microcontroladores.

Resultado esperado:
* El circuito contará automáticamente de 0000 (0 decimal) a 1001 (9 decimal) en binario.
* Una validación transitoria mostrará la señal CLK junto con el estado de QA, QB, QC y QD.
* Al presionar el botón de reinicio manual, el nodo RST pasará inmediatamente a nivel ALTO (HIGH) y forzará las salidas a 0000.
* Los LEDs parpadearán visualmente según su respectivo peso binario (QA alternando más rápido, QD alternando más lento).

Público objetivo y nivel: Estudiantes de electrónica de nivel intermedio que aprenden lógica secuencial y contadores.

Materiales

  • V1: Fuente de alimentación de 5 V DC
  • VCLK: Fuente de reloj de onda cuadrada de 5 V (1 Hz), función: entrada de reloj
  • U1: CI contador de década 74HC90
  • SW1: Pulsador (Normalmente Abierto), función: disparador de reinicio manual
  • R1: Resistencia de 10 kΩ, función: pull-down para el nodo RST
  • R2: Resistencia de 330 Ω, función: limitación de corriente del LED para QA
  • R3: Resistencia de 330 Ω, función: limitación de corriente del LED para QB
  • R4: Resistencia de 330 Ω, función: limitación de corriente del LED para QC
  • R5: Resistencia de 330 Ω, función: limitación de corriente del LED para QD
  • D1: LED rojo, función: indicador de QA (LSB)
  • D2: LED rojo, función: indicador de QB
  • D3: LED rojo, función: indicador de QC
  • D4: LED rojo, función: indicador de QD (MSB)

Pin-out del CI utilizado

74HC90 (Contador de década)

Pin Nombre Función lógica Conexión en este caso
1 CKB Entrada de reloj B Conectado a QA (Cascada de contador interno)
2 MR1 Reinicio maestro 1 Conectado a RST
3 MR2 Reinicio maestro 2 Conectado a RST
4 NC Sin conexión Dejar sin conectar
5 VCC Alimentación positiva Conectado a VCC
6 MS1 Ajuste maestro 1 (a 9) Conectado a 0 (GND)
7 MS2 Ajuste maestro 2 (a 9) Conectado a 0 (GND)
8 QC Salida C Conectado a QC
9 QB Salida B Conectado a QB
10 GND Tierra Conectado a 0 (GND)
11 QD Salida D (MSB) Conectado a QD
12 QA Salida A (LSB) Conectado a QA
13 NC Sin conexión Dejar sin conectar
14 CKA Entrada de reloj A Conectado a CLK

Guía de conexionado

  • V1: se conecta entre VCC y 0 (GND).
  • VCLK: se conecta entre CLK y 0.
  • SW1: se conecta entre VCC y RST.
  • R1: se conecta entre RST y 0.
  • U1 Pin 5 (VCC): se conecta a VCC.
  • U1 Pin 10 (GND): se conecta a 0.
  • U1 Pin 14 (CKA): se conecta a CLK.
  • U1 Pin 1 (CKB): se conecta a QA.
  • U1 Pin 2 (MR1): se conecta a RST.
  • U1 Pin 3 (MR2): se conecta a RST.
  • U1 Pin 6 (MS1): se conecta a 0.
  • U1 Pin 7 (MS2): se conecta a 0.
  • U1 Pin 12 (QA): se conecta a QA.
  • U1 Pin 9 (QB): se conecta a QB.
  • U1 Pin 8 (QC): se conecta a QC.
  • U1 Pin 11 (QD): se conecta a QD.
  • R2: se conecta entre QA y el nodo N_D1.
  • D1 (LED QA): se conecta entre N_D1 (ánodo) y 0 (cátodo).
  • R3: se conecta entre QB y el nodo N_D2.
  • D2 (LED QB): se conecta entre N_D2 (ánodo) y 0 (cátodo).
  • R4: se conecta entre QC y el nodo N_D3.
  • D3 (LED QC): se conecta entre N_D3 (ánodo) y 0 (cátodo).
  • R5: se conecta entre QD y el nodo N_D4.
  • D4 (LED QD): se conecta entre N_D4 (ánodo) y 0 (cátodo).

Diagrama de bloques conceptual

Conceptual block diagram — 74HC90 Decimal counter
Lectura rápida: entradas → bloque principal → salida (actuador o medida). Resume el esquemático ASCII de la siguiente sección.

Esquemático

[ VCLK: 1Hz Clock ] ------------------------(CLK)--> [ U1: 74HC90 Decade Counter ] --(QA)--> [ R2: 330 Ω ] --> [ D1: Red LED (LSB) ] --> GND
                                                     [                           ]
[ SW1: Button (VCC) + R1: Pull-down (GND) ] -(RST)-> [ CKA: CLK                  ] --(QB)--> [ R3: 330 Ω ] --> [ D2: Red LED ] --------> GND
                                                     [ MR1, MR2: RST             ]
[ GND ] ------------------------------------(0 V)---> [ MS1, MS2: GND             ] --(QC)--> [ R4: 330 Ω ] --> [ D3: Red LED ] --------> GND
                                                     [ CKB: Connects to QA       ]
                                                     [                           ] --(QD)--> [ R5: 330 Ω ] --> [ D4: Red LED (MSB) ] --> GND
Esquema Eléctrico


Mediciones y pruebas

  • Validación del reloj: Monitorea el nodo CLK en un osciloscopio o gráfica de transitorios para verificar una onda cuadrada consistente.
  • Secuencia de conteo: Grafica QA, QB, QC y QD simultáneamente. Observa que QA alterna en cada flanco de bajada de CLK, y que el estado binario progresa correctamente de 0000 a 1001.
  • Reinicio del ciclo BCD: Observa la transición después de 1001 (9 decimal). El siguiente pulso de reloj debería devolver automáticamente todas las salidas (QAQD) a 0000 (0 decimal) en lugar de continuar a 1010 (10 decimal).
  • Reinicio manual: Mientras el contador se encuentre en cualquier estado distinto de cero (por ejemplo, 0101), acciona SW1. Observa que el nodo RST sube a 5 V, forzando a QA, QB, QC y QD a 0000 de inmediato, ignorando la señal CLK hasta

Más Casos Prácticos en Prometeo.blog

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Netlist SPICE y simulación

Netlist SPICE de referencia (ngspice) — extractoNetlist SPICE completo (ngspice)

* 0 to 9 BCD Decimal Counter
.width out=256

* Power Supply
V1 VCC 0 DC 5

* Clock Source (1 Hz square wave)
VCLK CLK 0 PULSE(0 5 0 0.01 0.01 0.49 1)

* Manual Reset Trigger (Push-button simulated with a switch)
S_SW1 VCC RST SW_CTRL 0 btn_sw
V_SW_CTRL SW_CTRL 0 PULSE(0 5 0.1 0.01 0.01 0.2 20)
.model btn_sw SW(VT=2.5 RON=1 ROFF=100MEG)

* RST Pull-down Resistor
R1 RST 0 10k

* U1: 74HC90 Decade Counter (Behavioral Subcircuit)
X_U1 CLK QA RST RST 0 0 QA QB QC QD VCC 0 IC_7490

* ... (truncated in public view) ...

Copia este contenido en un archivo .cir y ejecútalo con ngspice.

* 0 to 9 BCD Decimal Counter
.width out=256

* Power Supply
V1 VCC 0 DC 5

* Clock Source (1 Hz square wave)
VCLK CLK 0 PULSE(0 5 0 0.01 0.01 0.49 1)

* Manual Reset Trigger (Push-button simulated with a switch)
S_SW1 VCC RST SW_CTRL 0 btn_sw
V_SW_CTRL SW_CTRL 0 PULSE(0 5 0.1 0.01 0.01 0.2 20)
.model btn_sw SW(VT=2.5 RON=1 ROFF=100MEG)

* RST Pull-down Resistor
R1 RST 0 10k

* U1: 74HC90 Decade Counter (Behavioral Subcircuit)
X_U1 CLK QA RST RST 0 0 QA QB QC QD VCC 0 IC_7490

* LEDs and Current Limiting Resistors
R2 QA N_D1 330
D1 N_D1 0 LED_RED

R3 QB N_D2 330
D2 N_D2 0 LED_RED

R4 QC N_D3 330
D3 N_D3 0 LED_RED

R5 QD N_D4 330
D4 N_D4 0 LED_RED

* Red LED Model
.model LED_RED D(IS=1e-19 N=1.6 RS=2.5)

* --- Subcircuits for 7490 Behavioral Model ---
.subckt IC_7490 CKA CKB MR1 MR2 MS1 MS2 QA QB QC QD VCC GND
* Dummy resistors to prevent floating pins if VCC/GND are unused internally
R_VCC VCC 0 100k
R_GND GND 0 1m

* Internal Reset/Set Logic
B_MR MR_int 0 V = (V(MR1)>2.5) * (V(MR2)>2.5) ? 5 : 0
B_MS MS_int 0 V = (V(MS1)>2.5) * (V(MS2)>2.5) ? 5 : 0
B_RST_bc RST_bc 0 V = ((V(MR_int)>2.5) + (V(MS_int)>2.5)) > 0.5 ? 5 : 0

* Divide-by-2 Section (FFa)
B_Da Da 0 V = V(QA)>2.5 ? 0 : 5
X_FFa CKA Da MR_int MS_int QA DFF_FALL

* Divide-by-5 Section (FFb, FFc, FFd)
B_Db Db 0 V = (V(QD)<2.5) * (V(QB)<2.5) ? 5 : 0
X_FFb CKB Db RST_bc 0 QB DFF_FALL

B_Dc Dc 0 V = ( (V(QB)>2.5)*(V(QC)<2.5) + (V(QB)<2.5)*(V(QC)>2.5) ) > 0.5 ? 5 : 0
X_FFc CKB Dc RST_bc 0 QC DFF_FALL

B_Dd Dd 0 V = (V(QB)>2.5) * (V(QC)>2.5) ? 5 : 0
X_FFd CKB Dd MR_int MS_int QD DFF_FALL
.ends

* Falling Edge Triggered D-Flip-Flop
.subckt DFF_FALL CLK D RST SET Q
B_CLK_B CLKB 0 V = V(CLK)>2.5 ? 0 : 5
X_Master CLK D RST SET QM DLATCH
X_Slave CLKB QM RST SET Q DLATCH
.ends

* Analog D-Latch with Reset and Set Priority
.subckt DLATCH EN D RST SET Q
B_Qnext Qnext 0 V = V(RST)>2.5 ? 0 : ( V(SET)>2.5 ? 5 : ( V(EN)>2.5 ? (V(D)>2.5 ? 5 : 0) : V(Q_int) ) )
R1 Qnext Q_int 100
C1 Q_int 0 10n
R2 Q_int 0 1G
E1 Q 0 Q_int 0 1
.ends
* ---------------------------------------------

* Simulation Commands
.op
.tran 0.01 12 0 1m
.print tran V(CLK) V(QA) V(QB) V(QC) V(QD) V(RST)
.end

Resultados de Simulación (Transitorio)

Resultados de Simulación (Transitorio)
Análisis: The transient analysis spans 0 s to 12 s and captures the switching interval. Main ranges: v(qa) -9.44 mV -> 5.01 V; v(qb) -8.98 mV -> 5.01 V; v(qc) -8.98 mV -> 5.01 V.
Show raw data table (12301 rows)
Index   time            v(clk)          v(qa)           v(qb)           v(qc)           v(qd)           v(rst)
0	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
1	1.000000e-04	5.000000e-02	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
2	2.000000e-04	1.000000e-01	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
3	4.000000e-04	2.000000e-01	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
4	8.000000e-04	4.000000e-01	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
5	1.600000e-03	8.000000e-01	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
6	2.600000e-03	1.300000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
7	3.600000e-03	1.800000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
8	4.600000e-03	2.300000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
9	5.600000e-03	2.800000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
10	6.310643e-03	3.155321e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
11	6.959607e-03	3.479804e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
12	7.789270e-03	3.894635e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
13	8.789270e-03	4.394635e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
14	9.789270e-03	4.894635e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
15	1.000000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
16	1.010000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
17	1.030000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
18	1.070000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
19	1.150000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
20	1.250000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
21	1.350000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
22	1.450000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
23	1.550000e-02	5.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	0.000000e+00	4.999500e-04
... (12277 more rows) ...

Quiz rápido

Pregunta 1: ¿Cuál es el objetivo principal del circuito descrito en el artículo?




Pregunta 2: ¿Qué rango de números decimales cuenta este circuito antes de reiniciarse?




Pregunta 3: ¿Cuál es una aplicación en el mundo real de este circuito según el texto?




Pregunta 4: ¿Qué sucede cuando se presiona el botón de reinicio manual?




Pregunta 5: ¿Qué componente impulsa el conteo del circuito?




Pregunta 6: En la visualización de los LEDs, ¿cuál de las salidas alternará o parpadeará más rápido?




Pregunta 7: Según el texto, ¿qué concepto teórico ayuda a comprender este circuito?




Pregunta 8: ¿A qué nivel de dificultad está dirigido este proyecto?




Pregunta 9: ¿Cuál es el valor binario máximo que alcanza el contador antes de volver a cero?




Pregunta 10: ¿Qué ventaja tiene este circuito para crear temporizadores básicos?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:


Caso práctico: Probador SPI para 74HC595 con ULX3S

Caso práctico: Probador SPI para 74HC595 con ULX3S — hero

Objetivo y caso de uso

Qué construirás: Un probador de salida de registro de desplazamiento SPI basado en hardware en la Radiona ULX3S que controla un CI 74HC595 externo y una barra de LED. Esta herramienta de prueba dedicada genera patrones de prueba SPI confiables para verificar la integridad de componentes externos y aislar fallas físicas de hardware de errores de firmware.

Por qué es importante / Casos de uso

  • Validación de ensamblaje de PCB: Prueba placas personalizadas para garantizar que todos los pines del 74HC595 estén soldados correctamente sin cortocircuitos ni puentes.
  • Comprobación de integridad de componentes: Verifica que un lote de CI de registro de desplazamiento sea completamente funcional antes de soldarlos en un producto final.
  • Depuración de señales SPI: Proporciona una señal maestra SPI continua y predecible (MOSI, SCK, CS) a frecuencias conocidas (por ejemplo, 1–10 MHz) para calibrar osciloscopios o analizadores lógicos.
  • Rodaje automatizado (Burn-in): Ejecuta patrones de bits alternos continuos en módulos de matriz de LED externos para probar la estabilidad del hardware a largo plazo.

Resultado esperado

  • Un flujo de bits maestro SPI verificado y en buen estado que se ejecuta en la FPGA ULX3S con una temporización predecible.
  • Confirmación visual de la integridad del hardware a través de un gráfico de barras LED externo controlado activamente que muestra patrones de prueba alternos.
  • Aislamiento claro de fallas entre el firmware de la FPGA, la temporización del protocolo SPI y el hardware físico de la PCB.

Audiencia: Ingenieros de hardware, desarrolladores de FPGA y diseñadores de PCB; Nivel: Intermedio

Arquitectura/flujo: La FPGA ULX3S genera una señal maestra SPI continua (SCK, MOSI, CS) enrutada a través de pines GPIO hacia un registro de desplazamiento 74HC595 externo, el cual decodifica los datos en serie en una salida paralela para controlar la barra de LED.

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. Para este perfil FPGA/ULX3S, los bloques de Verilog sintetizable se comprobaron con Yosys (read_verilog) y el conjunto Verilog de diseño/test se revisó con Verilator. El validador también comprobó la estructura de los bloques de código, que los comandos usen opciones copiables con guiones ASCII, que no aparezcan stacks no soportados y que esté disponible la toolchain ULX3S/ECP5 (yosys, nextpnr-ecp5, ecppack, openFPGALoader).

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 1 tablas y 5 bloques de código detectados antes de publicar.
  • Código comprobado: 3 Verilog/Yosys-Verilator, 1 Bash/copy-paste checks.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma compatibilidad sintáctica y de herramientas para el código publicado, pero no sustituye la prueba física sobre tu revisión exacta de ULX3S, tu archivo de restricciones de pines y tu cableado real.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de energizar la configuración, verifique la disposición de pines (pinout) de la revisión exacta de su placa ULX3S, mantenga las señales de E/S de la FPGA a 3.3 V, nunca conecte 5 V directamente a los pines de E/S, desconecte la alimentación antes de cambiar el cableado y use fuentes externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.

Requisitos previos

  • Comprensión básica de lógica digital (relojes, máquinas de estado, registros).
  • Familiaridad con el protocolo de Interfaz Periférica Serial (SPI) (específicamente MOSI, SCK y Chip Select/Latch).
  • Una instalación funcional de la cadena de herramientas de código abierto para FPGA Lattice ECP5: Yosys, nextpnr-ecp5, Project Trellis (ecppack) y openFPGALoader.
  • Verilator instalado para análisis (linting) y simulación.

Materiales

  • Placa FPGA: Radiona ULX3S (variante Lattice ECP5-85F).
  • Registro de desplazamiento: 1x CI de registro de desplazamiento de 8 bits 74HC595 (encapsulado DIP para protoboard).
  • Pantalla: 1x gráfico de barras LED de 10 segmentos (usaremos 8 segmentos).
  • Componentes pasivos: 8x resistencias de 330Ω.
  • Prototipado: 1x protoboard y un conjunto de cables puente macho-macho y macho-hembra.

Configuración/Conexión

El 74HC595 requiere que pines de control específicos estén conectados a niveles lógicos para funcionar correctamente. La ULX3S opera a una lógica de 3.3V, que es totalmente compatible con el VCC del 74HC595. Conecte los componentes de acuerdo con la siguiente tabla.

Pin ULX3S Puerto ULX3S Pin 74HC595 Descripción
3.3V 3.3V 16 (VCC) y 10 (SRCLR) Alimentación y borrado del registro de desplazamiento activo en bajo (conectado a alto)
GND GND 8 (GND) y 13 (OE) Tierra y habilitación de salida activa en bajo (conectado a bajo)
B11 gp[0] 14 (SER) SPI MOSI (Datos en serie)
C11 gp[1] 11 (SRCLK) SPI SCK (Reloj en serie)
A10 gp[2] 12 (RCLK) Latch SPI (Reloj del registro)

Para las salidas a la barra de LED:
1. Conecte los pines 15, 1, 2, 3, 4, 5, 6, 7 (Q0 a Q7) del 74HC595 a los ánodos de los primeros 8 segmentos de la barra de LED.
2. Conecte los 8 cátodos correspondientes de la barra de LED al riel de tierra a través de resistencias limitadoras de corriente de 330Ω.

Código validado

El proyecto está dividido en tres archivos Verilog sintetizables, un banco de pruebas (testbench) y un archivo de restricciones físicas. Manténgalos en el mismo directorio de trabajo.

spi_master.v

Este módulo maneja la serialización física de los datos de 8 bits. Espera una señal start, desplaza los datos empezando por el MSB (bit más significativo) en la línea MOSI, alterna la línea SCK y, finalmente, emite un pulso en la línea LATCH.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/* spi_master.v */
module spi_master (
    input wire clk,          // System clock
    input wire rst,          // Active high reset
    input wire start,        // Pulse high to start transmission
    input wire [7:0] data_in,// Data to transmit
    output reg mosi,         // Master Out Slave In
    output reg sck,          // Serial Clock
    output reg latch,        // Register Latch (RCLK)
    output reg busy          // High while transmitting
);

    reg [3:0] bit_cnt;
    reg [7:0] shift_reg;
    reg [2:0] state;

    localparam IDLE  = 3'd0;
    localparam LOAD  = 3'd1;
    localparam LOW   = 3'd2;
    localparam HIGH  = 3'd3;
    localparam LATCH = 3'd4;

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            mosi <= 1'b0;
            sck <= 1'b0;
            latch <= 1'b0;
            busy <= 1'b0;
            bit_cnt <= 4'd0;
            shift_reg <= 8'd0;
            state <= IDLE;
        end else begin
            case (state)
                IDLE: begin
                    latch <= 1'b0;
                    sck <= 1'b0;
                    if (start) begin
                        shift_reg <= data_in;
                        busy <= 1'b1;
// ...

/* spi_master.v */
module spi_master (
    input wire clk,          // System clock
    input wire rst,          // Active high reset
    input wire start,        // Pulse high to start transmission
    input wire [7:0] data_in,// Data to transmit
    output reg mosi,         // Master Out Slave In
    output reg sck,          // Serial Clock
    output reg latch,        // Register Latch (RCLK)
    output reg busy          // High while transmitting
);

    reg [3:0] bit_cnt;
    reg [7:0] shift_reg;
    reg [2:0] state;

    localparam IDLE  = 3'd0;
    localparam LOAD  = 3'd1;
    localparam LOW   = 3'd2;
    localparam HIGH  = 3'd3;
    localparam LATCH = 3'd4;

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            mosi <= 1'b0;
            sck <= 1'b0;
            latch <= 1'b0;
            busy <= 1'b0;
            bit_cnt <= 4'd0;
            shift_reg <= 8'd0;
            state <= IDLE;
        end else begin
            case (state)
                IDLE: begin
                    latch <= 1'b0;
                    sck <= 1'b0;
                    if (start) begin
                        shift_reg <= data_in;
                        busy <= 1'b1;
                        bit_cnt <= 4'd8;
                        state <= LOAD;
                    end else begin
                        busy <= 1'b0;
                    end
                end

                LOAD: begin
                    mosi <= shift_reg[7];
                    shift_reg <= {shift_reg[6:0], 1'b0};
                    sck <= 1'b0;
                    state <= HIGH;
                end

                HIGH: begin
                    sck <= 1'b1;
                    bit_cnt <= bit_cnt - 4'd1;
                    state <= LOW;
                end

                LOW: begin
                    sck <= 1'b0;
                    if (bit_cnt == 4'd0) begin
                        state <= LATCH;
                    end else begin
                        state <= LOAD;
                    end
                end

                LATCH: begin
                    latch <= 1'b1;
                    state <= IDLE;
                end

                default: state <= IDLE;
            endcase
        end
    end
endmodule

tester_top.v

Este es el módulo de nivel superior. Divide el reloj de 25 MHz de la ULX3S para crear un tick de actualización para los patrones y un reloj de ~3 MHz para el maestro SPI (resultando en un SCK de ~1.5 MHz). También elimina el rebote (debounce) del botón de usuario y alterna entre cuatro patrones de prueba.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/* tester_top.v */
module tester_top #(
    parameter TIMER_BITS = 24 // Can be reduced for simulation speed
)(
    input wire clk_25mhz,
    input wire [2:1] btn,
    output wire spi_mosi,
    output wire spi_sck,
    output wire spi_latch
);

    wire rst = btn[1];
    wire mode_btn = btn[2];

    // Clock dividers
    reg [TIMER_BITS-1:0] timer;
    reg [3:0] spi_clk_div;
    wire spi_clk = spi_clk_div[3];

    always @(posedge clk_25mhz) begin
        timer <= timer + 1;
        spi_clk_div <= spi_clk_div + 4'd1;
    end

    wire update_tick = (timer == 0);

    // Button Debouncer
    reg [15:0] debounce_shift;
    reg mode_btn_clean;
    reg mode_btn_last;
    wire mode_pulse;

    always @(posedge clk_25mhz) begin
        debounce_shift <= {debounce_shift[14:0], mode_btn};
        if (debounce_shift == 16'hFFFF) mode_btn_clean <= 1'b1;
        else if (debounce_shift == 16'h0000) mode_btn_clean <= 1'b0;

        mode_btn_last <= mode_btn_clean;
    end

    assign mode_pulse = mode_btn_clean & ~mode_btn_last;

    // Pattern Generator State Machine
    reg [1:0] mode;
    reg [7:0] current_pattern;
    reg [2:0] walk_idx;

    always @(posedge clk_25mhz or posedge rst) begin
        if (rst) begin
            mode <= 2'd0;
            walk_idx <= 3'd0;
// ...

/* tester_top.v */
module tester_top #(
    parameter TIMER_BITS = 24 // Can be reduced for simulation speed
)(
    input wire clk_25mhz,
    input wire [2:1] btn,
    output wire spi_mosi,
    output wire spi_sck,
    output wire spi_latch
);

    wire rst = btn[1];
    wire mode_btn = btn[2];

    // Clock dividers
    reg [TIMER_BITS-1:0] timer;
    reg [3:0] spi_clk_div;
    wire spi_clk = spi_clk_div[3];

    always @(posedge clk_25mhz) begin
        timer <= timer + 1;
        spi_clk_div <= spi_clk_div + 4'd1;
    end

    wire update_tick = (timer == 0);

    // Button Debouncer
    reg [15:0] debounce_shift;
    reg mode_btn_clean;
    reg mode_btn_last;
    wire mode_pulse;

    always @(posedge clk_25mhz) begin
        debounce_shift <= {debounce_shift[14:0], mode_btn};
        if (debounce_shift == 16'hFFFF) mode_btn_clean <= 1'b1;
        else if (debounce_shift == 16'h0000) mode_btn_clean <= 1'b0;

        mode_btn_last <= mode_btn_clean;
    end

    assign mode_pulse = mode_btn_clean & ~mode_btn_last;

    // Pattern Generator State Machine
    reg [1:0] mode;
    reg [7:0] current_pattern;
    reg [2:0] walk_idx;

    always @(posedge clk_25mhz or posedge rst) begin
        if (rst) begin
            mode <= 2'd0;
            walk_idx <= 3'd0;
            current_pattern <= 8'h01;
        end else begin
            if (mode_pulse) begin
                mode <= mode + 2'd1;
                walk_idx <= 3'd0;
            end

            if (update_tick) begin
                case (mode)
                    2'd0: begin // Mode 0: Walking One
                        current_pattern <= (8'h01 << walk_idx);
                        walk_idx <= walk_idx + 3'd1;
                    end
                    2'd1: begin // Mode 1: Alternating
                        current_pattern <= (walk_idx[0]) ? 8'hAA : 8'h55;
                        walk_idx <= walk_idx + 3'd1;
                    end
                    2'd2: begin // Mode 2: All On
                        current_pattern <= 8'hFF;
                    end
                    2'd3: begin // Mode 3: All Off
                        current_pattern <= 8'h00;
                    end
                endcase
            end
        end
    end

    // SPI Master Instantiation
    wire spi_busy;
    reg spi_start;
    reg spi_start_d;

    // Generate a single-cycle start pulse in the spi_clk domain
    always @(posedge spi_clk) begin
        spi_start_d <= update_tick;
        spi_start <= update_tick & ~spi_start_d;
    end

    spi_master u_spi (
        .clk(spi_clk),
        .rst(rst),
        .start(spi_start),
        .data_in(current_pattern),
        .mosi(spi_mosi),
        .sck(spi_sck),
        .latch(spi_latch),
        .busy(spi_busy)
    );

endmodule

tb_tester.v

Un banco de pruebas Verilog puro utilizado por Verilator para simular la FSM (máquina de estados finitos) y la generación de salida SPI. El parámetro TIMER_BITS se anula para acelerar la simulación.

/* tb_tester.v */
`timescale 1ns/1ps

module tb_tester;
    reg clk;
    reg [2:1] btn;
    wire mosi, sck, latch;

    // Instantiate with a small timer for fast simulation
    tester_top #(.TIMER_BITS(6)) dut (
        .clk_25mhz(clk),
        .btn(btn),
        .spi_mosi(mosi),
        .spi_sck(sck),
        .spi_latch(latch)
    );

    initial begin
        $dumpfile("tester_sim.vcd");
        $dumpvars(0, tb_tester);

        clk = 0;
        btn = 2'b10; // Assert reset (btn[1])
        #100;
        btn = 2'b00; // Release reset

        // Simulate enough time to observe the SPI transmission
        #15000;

        $display("Simulation complete. Check VCD for SPI waveforms.");
        $finish;
    end

    always #20 clk = ~clk; // 25MHz clock (40ns period)

endmodule

ulx3s.lpf

El archivo de restricciones físicas que mapea los puertos lógicos a los pines del ECP5 en la ULX3S.

# ulx3s.lpf
LOCATE COMP "clk_25mhz" SITE "G2";
IOBUF COMP "clk_25mhz" IO_TYPE=LVCMOS33;

# Buttons
LOCATE COMP "btn[1]" SITE "R1";
IOBUF COMP "btn[1]" IO_TYPE=LVCMOS33;
LOCATE COMP "btn[2]" SITE "T1";
IOBUF COMP "btn[2]" IO_TYPE=LVCMOS33;

# SPI Outputs to 74HC595
LOCATE COMP "spi_mosi" SITE "B11";
IOBUF COMP "spi_mosi" IO_TYPE=LVCMOS33;
LOCATE COMP "spi_sck" SITE "C11";
IOBUF COMP "spi_sck" IO_TYPE=LVCMOS33;
LOCATE COMP "spi_latch" SITE "A10";
IOBUF COMP "spi_latch" IO_TYPE=LVCMOS33;

Comandos de compilación y programación

Utilice los siguientes comandos del flujo canónico de código abierto para simular, compilar y programar el diseño en su ULX3S.

# 1. Lint and Simulate using Verilator
verilator --lint-only -Wno-DECLFILENAME spi_master.v tester_top.v
verilator --binary --trace -Wno-DECLFILENAME spi_master.v tester_top.v tb_tester.v
./obj_dir/Vtb_tester

# 2. Synthesize using Yosys
yosys -p "synth_ecp5 -top tester_top -json tester.json" spi_master.v tester_top.v

# 3. Place and Route using nextpnr-ecp5 (targeting ECP5-85F)
nextpnr-ecp5 --85k --package CABGA381 --json tester.json --lpf ulx3s.lpf --textcfg tester_out.config

# 4. Pack the bitstream using ecppack
ecppack tester_out.config tester.bit

# 5. Program the ULX3S using openFPGALoader
openFPGALoader -b ulx3s tester.bit

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Qué dispositivo principal se utiliza para construir el probador de salida de registro de desplazamiento SPI?




Pregunta 2: ¿Qué circuito integrado (CI) externo controla el probador de salida de registro de desplazamiento SPI?




Pregunta 3: ¿Cuál es el propósito principal de esta herramienta de prueba dedicada?




Pregunta 4: ¿Qué permite verificar la validación de ensamblaje de PCB en este contexto?




Pregunta 5: ¿Para qué sirve la comprobación de integridad de componentes mencionada en el texto?




Pregunta 6: ¿Qué señales proporciona el probador para la depuración de señales SPI?




Pregunta 7: ¿En qué rango de frecuencias se generan las señales SPI para calibrar osciloscopios o analizadores lógicos?




Pregunta 8: ¿Qué componente visual se utiliza junto con el CI 74HC595 en este proyecto?




Pregunta 9: ¿En qué consiste el rodaje automatizado (Burn-in) descrito en el caso de uso?




Pregunta 10: ¿Qué tipo de fallas ayuda a aislar esta herramienta de prueba?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:


Caso práctico: medidor de RPM con encoder en ULX3S

Medidor de RPM con encoder en ULX3S

Construcción de un medidor de RPM con codificador de cuadratura usando Lattice ECP5

Objetivo y caso de uso

Qué construirás: Un medidor digital de Revoluciones Por Minuto (RPM) que lee un codificador mecánico de cuadratura utilizando una FPGA Lattice ECP5, calculando la velocidad de rotación y mostrándola en tiempo real con una latencia inferior a un milisegundo en una barra de LED externa.

Por qué es importante / Casos de uso

  • Monitorización de cintas transportadoras de baja velocidad: Proporciona verificación independiente de las velocidades de la cinta en la línea de montaje con una latencia <1ms, actuando como un monitor fiable de ejes de baja velocidad.
  • Pruebas de manivelas/cabrestantes manuales: Actúa como una ayuda de medición para asegurar que los operadores no excedan las velocidades seguras de enrollado manual (ej. 0-120 RPM) en polipastos mecánicos o tensores de laboratorio.
  • Ajuste de motores paso a paso: Ofrece verificación física e independiente de la velocidad de rotación real durante el ajuste de baja frecuencia, omitiendo las estimaciones de software del microcontrolador.
  • Procesamiento de señales por hardware: Demuestra conceptos cruciales de FPGA, incluyendo la sincronización de registros de desplazamiento, la mitigación de rebotes de interruptores mecánicos (filtro de ~5ms) y la medición de frecuencia en ventanas de tiempo fijas.

Resultado esperado

  • Un módulo Verilog robusto que muestrea continuamente los canales A y B del codificador KY-040 a 50MHz sin metaestabilidad.
  • Una máquina de estados digital que filtra el rebote mecánico y decodifica con precisión la dirección de los pasos de cuadratura.
  • Un cálculo de RPM en tiempo real y una tubería (pipeline) de visualización en barra de LED que utiliza <1% de los recursos lógicos de la FPGA ECP5 (LUTs).

Audiencia: Ingenieros de hardware integrado y desarrolladores de FPGA; Nivel: Intermedio

Arquitectura/flujo: Codificador KY-040 → Sincronizador de Flip-Flop de 2 etapas → Filtro antirrebote → Decodificador de cuadratura → Contador de RPM de ventana fija → Controlador de visualización de barra de LED

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. Para este perfil FPGA/ULX3S, los bloques de Verilog sintetizable se comprobaron con Yosys (read_verilog) y el conjunto Verilog de diseño/test se revisó con Verilator. El validador también comprobó la estructura de los bloques de código, que los comandos usen opciones copiables con guiones ASCII, que no aparezcan stacks no soportados y que esté disponible la toolchain ULX3S/ECP5 (yosys, nextpnr-ecp5, ecppack, openFPGALoader).

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 1 tablas y 3 bloques de código detectados antes de publicar.
  • Código comprobado: 2 Verilog/Yosys-Verilator.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma compatibilidad sintáctica y de herramientas para el código publicado, pero no sustituye la prueba física sobre tu revisión exacta de ULX3S, tu archivo de restricciones de pines y tu cableado real.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender la configuración, verifica la disposición de pines de tu revisión exacta de la placa ULX3S, mantén las señales de E/S de la FPGA a 3.3 V, nunca conectes 5 V directamente a los pines de E/S, desconecta la alimentación antes de cambiar el cableado y utiliza fuentes externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Codificador KY-040

Sincronizador de Flip-Flop de 2 etapas

Filtro antirrebote

Decodificador de cuadratura

Contador de RPM de ventana fija

Controlador de visualización de barra de LED

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Código fuente

Verilator

Yosys

Implementación hardware

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Configuración de hardware

Este tutorial está dirigido a la Radiona ULX3S con una FPGA Lattice ECP5-85F.

Nombre de señal Pin ULX3S Estándar de E/S Conexión de hardware
clk_25mhz G2 LVCMOS33 Oscilador en placa de 25MHz
enc_a B11 LVCMOS33 (Pull-up) Pin CLK de KY-040 (GPIO gp[0])
enc_b C11 LVCMOS33 (Pull-up) Pin DT de KY-040 (GPIO gp[1])
led[0] H3 LVCMOS33 LED 0 en placa
led[1] E1 LVCMOS33 LED 1 en placa
led[2] E2 LVCMOS33 LED 2 en placa
led[3] D1 LVCMOS33 LED 3 en placa
led[4] D2 LVCMOS33 LED 4 en placa
led[5] C1 LVCMOS33 LED 5 en placa
led[6] C2 LVCMOS33 LED 6 en placa
led[7] B2 LVCMOS33 LED 7 en placa

Implementación en Verilog

El diseño utiliza una ventana de tiempo de 1 segundo generada a partir del reloj del sistema de 25MHz. Sincroniza las entradas asíncronas del codificador, mitiga los rebotes y cuenta los pulsos. Un codificador KY-040 estándar genera 20 pulsos por revolución.

Código fuente sintetizable

Guarda el siguiente código como top.v.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/* top.v */
module top #(
    parameter CLK_FREQ = 25000000,
    parameter DEBOUNCE_CYCLES = 50000
) (
    input  wire clk_25mhz,
    input  wire enc_a,
    input  wire enc_b,
    output wire [7:0] led
);

    // Synchronization registers to prevent metastability
    reg [2:0] sync_a;
    always @(posedge clk_25mhz) begin
        sync_a <= {sync_a[1:0], enc_a};
    end

    // Debounce logic for Channel A
    reg clean_a;
    reg [15:0] debounce_cnt_a;

    initial begin
        clean_a = 1'b1;
        debounce_cnt_a = 16'd0;
    end

    always @(posedge clk_25mhz) begin
        if (sync_a[2] == clean_a) begin
            debounce_cnt_a <= 16'd0;
        end else begin
            debounce_cnt_a <= debounce_cnt_a + 16'd1;
            if (debounce_cnt_a == DEBOUNCE_CYCLES) begin
                clean_a <= sync_a[2];
                debounce_cnt_a <= 16'd0;
            end
        end
    end

    // Edge detection on debounced Channel A
    reg clean_a_prev;
    initial clean_a_prev = 1'b1;

    always @(posedge clk_25mhz) begin
        clean_a_prev <= clean_a;
    end
// ...

/* top.v */
module top #(
    parameter CLK_FREQ = 25000000,
    parameter DEBOUNCE_CYCLES = 50000
) (
    input  wire clk_25mhz,
    input  wire enc_a,
    input  wire enc_b,
    output wire [7:0] led
);

    // Synchronization registers to prevent metastability
    reg [2:0] sync_a;
    always @(posedge clk_25mhz) begin
        sync_a <= {sync_a[1:0], enc_a};
    end

    // Debounce logic for Channel A
    reg clean_a;
    reg [15:0] debounce_cnt_a;

    initial begin
        clean_a = 1'b1;
        debounce_cnt_a = 16'd0;
    end

    always @(posedge clk_25mhz) begin
        if (sync_a[2] == clean_a) begin
            debounce_cnt_a <= 16'd0;
        end else begin
            debounce_cnt_a <= debounce_cnt_a + 16'd1;
            if (debounce_cnt_a == DEBOUNCE_CYCLES) begin
                clean_a <= sync_a[2];
                debounce_cnt_a <= 16'd0;
            end
        end
    end

    // Edge detection on debounced Channel A
    reg clean_a_prev;
    initial clean_a_prev = 1'b1;

    always @(posedge clk_25mhz) begin
        clean_a_prev <= clean_a;
    end

    wire step_pulse = (clean_a == 1'b1 && clean_a_prev == 1'b0);

    // 1-Second Timer and Pulse Counter
    reg [24:0] timer;
    reg [15:0] pulse_count;
    reg [15:0] saved_pulses;

    initial begin
        timer = 25'd0;
        pulse_count = 16'd0;
        saved_pulses = 16'd0;
    end

    always @(posedge clk_25mhz) begin
        if (timer == CLK_FREQ - 1) begin
            timer <= 25'd0;
            saved_pulses <= pulse_count;
            pulse_count <= 16'd0;
        end else begin
            timer <= timer + 25'd1;
            if (step_pulse) begin
                pulse_count <= pulse_count + 16'd1;
            end
        end
    end

    // Calculate RPM: (Pulses per second * 60) / 20 pulses per revolution = Pulses * 3
    wire [15:0] rpm = saved_pulses * 16'd3;

    // LED Bar Graph Display (Thermometer Code)
    reg [7:0] led_reg;
    always @(*) begin
        led_reg = 8'b00000000;
        if (rpm >= 16'd15)  led_reg[0] = 1'b1;
        if (rpm >= 16'd30)  led_reg[1] = 1'b1;
        if (rpm >= 16'd45)  led_reg[2] = 1'b1;
        if (rpm >= 16'd60)  led_reg[3] = 1'b1;
        if (rpm >= 16'd75)  led_reg[4] = 1'b1;
        if (rpm >= 16'd90)  led_reg[5] = 1'b1;
        if (rpm >= 16'd105) led_reg[6] = 1'b1;
        if (rpm >= 16'd120) led_reg[7] = 1'b1;
    end

    assign led = led_reg;

endmodule

Banco de pruebas

Guarda el siguiente código como tb_top.v. El banco de pruebas sobrescribe los parámetros del módulo para acortar drásticamente la ventana de conteo de 1 segundo a fin de obtener tiempos de simulación prácticos.

/* tb_top.v */
`timescale 1ns/1ps

module tb_top;
    reg clk;
    reg enc_a;
    reg enc_b;
    wire [7:0] led;

    // Instantiate with simulation-friendly parameters
    // CLK_FREQ = 2500 (100us window instead of 1s)
    // DEBOUNCE_CYCLES = 5 (instead of 50000)
    top #(
        .CLK_FREQ(2500),
        .DEBOUNCE_CYCLES(5)
    ) dut (
        .clk_25mhz(clk),
        .enc_a(enc_a),
        .enc_b(enc_b),
        .led(led)
    );

    initial begin
        clk = 0;
        forever #20 clk = ~clk; // 25MHz clock (40ns period)
    end

    initial begin
        $dumpfile("tb_top.vcd");
        $dumpvars(0, tb_top);

        enc_a = 1;
        enc_b = 1;

        #1000;

        // Simulate multiple encoder steps to register an RPM
        repeat (10) begin
            enc_a = 0; #400; // Exceeds debounce (5 * 40ns = 200ns)
            enc_b = 0; #400;
            enc_a = 1; #400;
            enc_b = 1; #400;
        end

        // Wait for the timer window to expire (100us = 100,000ns)
        #150000;

        $finish;
    end
endmodule

Restricciones de pines

Guarda el siguiente código como ulx3s.lpf.

# ulx3s.lpf
LOCATE COMP "clk_25mhz" SITE "G2";
IOBUF PORT "clk_25mhz" PULLMODE=NONE IO_TYPE=LVCMOS

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Cuál es el objetivo principal del proyecto descrito en el texto?




Pregunta 2: ¿Qué tipo de codificador se utiliza en este proyecto para medir las revoluciones?




Pregunta 3: ¿Cuál es la latencia máxima esperada para mostrar la velocidad de rotación?




Pregunta 4: ¿Dónde se muestra la velocidad de rotación calculada en tiempo real?




Pregunta 5: ¿Cuál es uno de los casos de uso mencionados para este medidor de RPM?




Pregunta 6: En el contexto de pruebas de manivelas o cabrestantes manuales, ¿cuál es el rango de velocidad segura mencionado como ejemplo?




Pregunta 7: ¿Qué ventaja ofrece este sistema al ajustar motores paso a paso?




Pregunta 8: ¿Qué estimaciones permite omitir este medidor al ajustar motores paso a paso?




Pregunta 9: ¿Qué tipo de procesamiento se destaca como importante en el último punto del texto?




Pregunta 10: ¿Qué componente principal actúa como el 'cerebro' para calcular la velocidad en este proyecto?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:


Caso práctico: entrenador de tiempo de reacción con ULX3S

Entrenador de tiempo de reacción con ULX3S

Objetivo y caso de uso

Qué construirás: Un entrenador de tiempo de reacción de hardware autónomo que utiliza un Registro de Desplazamiento con Retroalimentación Lineal (LFSR) para retardos aleatorios y un temporizador de hardware de precisión para medir la velocidad de los reflejos humanos.

Por qué es importante / Casos de uso

  • Prototipado de hardware: Sirve como un demostrador de hardware base para objetivos de reflejos personalizados o temporizadores de eventos que requieren precisión a nivel de milisegundos.
  • Pruebas de humanos en el bucle (Human-in-the-loop): Actúa como una herramienta educativa repetible para demostrar cómo la distracción afecta los tiempos de reacción en un entorno de aficionado controlado.
  • Validación de hardware: Ofrece una alternativa determinista a los temporizadores basados en software. Método de validación: La afirmación de precisión (precisión de 40 ns) se valida ejecutando el banco de pruebas de Verilator incluido e inspeccionando la forma de onda VCD resultante. Evidencia esperada: GTKWave mostrará un delta exacto de 40 ns (un ciclo de reloj de 25 MHz) entre el tic del temporizador interno y la actualización del registro de salida LED, demostrando cero fluctuación (jitter) inducida por el software.
  • Educación sobre lógica integrada: Demuestra la integración práctica de máquinas de estados de hardware y pseudoaleatoriedad a través de LFSRs.

Resultado esperado

  • Un sistema de medición de reflejos completamente funcional capaz de lograr una precisión de temporización determinista de hasta 40 ns por tic.
  • Simulaciones de formas de onda de Verilator verificadas que confirman la ausencia de latencia inducida por software.

Nota de seguridad: Este proyecto es un prototipo estrictamente educativo. No está diseñado, previsto ni certificado para evaluaciones de salud, evaluaciones de preparación críticas para la seguridad o cualquier aplicación profesional de monitoreo humano.

Audiencia: Estudiantes de sistemas embebidos, desarrolladores de FPGA e ingenieros de prototipado de hardware; Nivel: Intermedio

Arquitectura/flujo: Un LFSR genera un período de espera pseudoaleatorio → El estímulo se activa → El contador de hardware de 25 MHz comienza a contar a intervalos de 40 ns → La entrada del usuario detiene el contador → Se calcula y se muestra el tiempo de reacción determinista.

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. Para este perfil FPGA/ULX3S, los bloques de Verilog sintetizable se comprobaron con Yosys (read_verilog) y el conjunto Verilog de diseño/test se revisó con Verilator. El validador también comprobó la estructura de los bloques de código, que los comandos usen opciones copiables con guiones ASCII, que no aparezcan stacks no soportados y que esté disponible la toolchain ULX3S/ECP5 (yosys, nextpnr-ecp5, ecppack, openFPGALoader).

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 1 tablas y 4 bloques de código detectados antes de publicar.
  • Código comprobado: 2 Verilog/Yosys-Verilator, 1 Bash/copy-paste checks.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma compatibilidad sintáctica y de herramientas para el código publicado, pero no sustituye la prueba física sobre tu revisión exacta de ULX3S, tu archivo de restricciones de pines y tu cableado real.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender la configuración, verifique el pinout de la revisión exacta de su placa ULX3S, mantenga las señales de E/S de la FPGA a 3.3 V, nunca conecte 5 V directamente a los pines de E/S, desconecte la alimentación antes de cambiar el cableado y use fuentes externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Un LFSR genera un período de espera pseud…

El estímulo se activa

El contador de hardware de 25 MHz comienz…

La entrada del usuario detiene el contador

Se calcula y se muestra el tiempo de reac…

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Código fuente

Verilator

Yosys

Implementación hardware

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Requisitos previos

Para compilar, simular y grabar este proyecto, necesita la cadena de herramientas canónica de código abierto ECP5 instalada en su estación de trabajo Linux o macOS:
* Verilator: Para el análisis estático (linting) y la simulación del diseño Verilog.
* Yosys: Para sintetizar el código Verilog en RTL.
* nextpnr-ecp5: Para la ubicación y ruteo del diseño sintetizado para la arquitectura Lattice ECP5.
* Project Trellis (ecppack): Para empaquetar el diseño ruteado en un bitstream .bit grabable.
* openFPGALoader: Para programar el bitstream en la placa Radiona ULX3S.
* Make/Bash: Para ejecutar los comandos de compilación.

Materiales

  • Placa FPGA: Radiona ULX3S (específicamente la variante Lattice ECP5-85F, aunque el código es compatible con las variantes 12F y 45F si se ajusta la bandera del dispositivo en nextpnr).
  • Cable: Un cable de datos USB Tipo C estándar para alimentación y programación.

Configuración y asignación de pines

Dado que este proyecto depende completamente de los periféricos integrados en la Radiona ULX3S, no es necesario ningún cableado externo. Las conexiones se definen lógicamente en el archivo de restricciones.

Nombre de la señal Pin de ULX3S Tipo de E/S Descripción
clk_25mhz G2 LVCMOS33 Oscilador integrado de 25 MHz
btn_start R1 LVCMOS33 Botón arriba (btn[1]), Activo en alto
btn_react T1 LVCMOS33 Botón abajo (btn[2]), Activo en alto
led[0] B2 LVCMOS33 LED 0 (LSB)
led[1] C2 LVCMOS33 LED 1
led[2] C1 LVCMOS33 LED 2
led[3] D2 LVCMOS33 LED 3
led[4] D1 LVCMOS33 LED 4
led[5] E2 LVCMOS33 LED 5
led[6] E1 LVCMOS33 LED 6
led[7] H3 LVCMOS33 LED 7 (MSB)

El flujo lógico se basa en una Máquina de Estados Finitos (FSM):
* Estado IDLE (Inactivo): Los LEDs exteriores parpadean para indicar que está listo. Esperando a btn_start.
* Estado WAIT (Espera): Los LEDs se apagan. El sistema espera una duración aleatoria (entre 1 y ~4 segundos) generada por un LFSR interno. Si se presiona btn_react en este momento, se desencadena un estado de FAULT (Fallo).
* Estado TEST (Prueba): Todos los LEDs se encienden. Comienza un contador de milisegundos. El sistema espera a btn_react.
* Estado RESULT (Resultado): El tiempo de reacción se muestra en el gráfico de barras LED. Cada LED iluminado representa un bloque de 50 ms a partir de 100 ms.
* Estado FAULT (Fallo): Los LEDs parpadean rápidamente para indicar una salida en falso. Esperando a btn_start para reiniciar.

Archivos fuente del proyecto

El proyecto consta de cuatro archivos: el módulo Verilog sintetizable, el banco de pruebas Verilog para la simulación, el archivo de restricciones LPF y un script de compilación en Bash. Manténgalos en el mismo directorio de trabajo.

1. Verilog sintetizable: reaction_trainer.v

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/*
 * Module: reaction_trainer
 * Description: A hardware reaction time measurement tool for the Radiona ULX3S.
 */
module reaction_trainer #(
    parameter MS_TICKS = 25000,      // 25MHz clock: 25,000 ticks = 1ms
    parameter DEBOUNCE_TICKS = 250000 // 10ms debounce
)(
    input  wire clk_25mhz,
    input  wire btn_start,
    input  wire btn_react,
    output reg  [7:0] led
);

    // --- State Machine Definitions ---
    localparam S_IDLE   = 3'd0;
    localparam S_WAIT   = 3'd1;
    localparam S_TEST   = 3'd2;
    localparam S_RESULT = 3'd3;
    localparam S_FAULT  = 3'd4;

    reg [2:0] state = S_IDLE;

    // --- Button Debouncing ---
    reg [17:0] debounce_counter_start = 0;
    reg [17:0] debounce_counter_react = 0;
    reg start_clean = 0;
    reg react_clean = 0;
    reg start_sync_1 = 0, start_sync_2 = 0;
    reg react_sync_1 = 0, react_sync_2 = 0;

    always @(posedge clk_25mhz) begin
        // Double-flop synchronizers
        start_sync_1 <= btn_start; start_sync_2 <= start_sync_1;
        react_sync_1 <= btn_react; react_sync_2 <= react_sync_1;

        // Debounce btn_start
        if (start_sync_2 == start_clean) begin
            debounce_counter_start <= 0;
        end else begin
            debounce_counter_start <= debounce_counter_start + 1;
            if (debounce_counter_start >= DEBOUNCE_TICKS) begin
                start_clean <= start_sync_2;
                debounce_counter_start <= 0;
            end
        end

        // Debounce btn_react
        if (react_sync_2 == react_clean) begin
            debounce_counter_react <= 0;
        end else begin
            debounce_counter_react <= debounce_counter_react + 1;
            if (debounce_counter_react >= DEBOUNCE_TICKS) begin
                react_clean <= react_sync_2;
                debounce_counter_react <= 0;
            end
        end
    end

    // Edge detection for debounced buttons
    reg start_clean_last = 0;
    reg react_clean_last = 0;
    wire start_pressed = (start_clean && !start_clean_last);
    wire react_pressed = (react_clean && !react_clean_last);

    always @(posedge clk_25mhz) begin
        start_clean_last <= start_clean;
        react_clean_last <= react_clean;
    end

    // --- Timers and LFSR ---
    reg [15:0] ms_counter = 0;
    reg [15:0] timer_ms = 0;
    reg [15:0] random_wait_ms = 0;

    // 16-bit LFSR for pseudo-randomness
    reg [15:0] lfsr = 16'hACE1;

    always @(posedge clk_25mhz) begin
        // Continuously run LFSR to ensure entropy based on human timing
        lfsr <= {lfsr[14:0], lfsr[15] ^ lfsr[13] ^ lfsr[12] ^ lfsr[10]};
    end
// ...

/*
 * Module: reaction_trainer
 * Description: A hardware reaction time measurement tool for the Radiona ULX3S.
 */
module reaction_trainer #(
    parameter MS_TICKS = 25000,      // 25MHz clock: 25,000 ticks = 1ms
    parameter DEBOUNCE_TICKS = 250000 // 10ms debounce
)(
    input  wire clk_25mhz,
    input  wire btn_start,
    input  wire btn_react,
    output reg  [7:0] led
);

    // --- State Machine Definitions ---
    localparam S_IDLE   = 3'd0;
    localparam S_WAIT   = 3'd1;
    localparam S_TEST   = 3'd2;
    localparam S_RESULT = 3'd3;
    localparam S_FAULT  = 3'd4;

    reg [2:0] state = S_IDLE;

    // --- Button Debouncing ---
    reg [17:0] debounce_counter_start = 0;
    reg [17:0] debounce_counter_react = 0;
    reg start_clean = 0;
    reg react_clean = 0;
    reg start_sync_1 = 0, start_sync_2 = 0;
    reg react_sync_1 = 0, react_sync_2 = 0;

    always @(posedge clk_25mhz) begin
        // Double-flop synchronizers
        start_sync_1 <= btn_start; start_sync_2 <= start_sync_1;
        react_sync_1 <= btn_react; react_sync_2 <= react_sync_1;

        // Debounce btn_start
        if (start_sync_2 == start_clean) begin
            debounce_counter_start <= 0;
        end else begin
            debounce_counter_start <= debounce_counter_start + 1;
            if (debounce_counter_start >= DEBOUNCE_TICKS) begin
                start_clean <= start_sync_2;
                debounce_counter_start <= 0;
            end
        end

        // Debounce btn_react
        if (react_sync_2 == react_clean) begin
            debounce_counter_react <= 0;
        end else begin
            debounce_counter_react <= debounce_counter_react + 1;
            if (debounce_counter_react >= DEBOUNCE_TICKS) begin
                react_clean <= react_sync_2;
                debounce_counter_react <= 0;
            end
        end
    end

    // Edge detection for debounced buttons
    reg start_clean_last = 0;
    reg react_clean_last = 0;
    wire start_pressed = (start_clean && !start_clean_last);
    wire react_pressed = (react_clean && !react_clean_last);

    always @(posedge clk_25mhz) begin
        start_clean_last <= start_clean;
        react_clean_last <= react_clean;
    end

    // --- Timers and LFSR ---
    reg [15:0] ms_counter = 0;
    reg [15:0] timer_ms = 0;
    reg [15:0] random_wait_ms = 0;

    // 16-bit LFSR for pseudo-randomness
    reg [15:0] lfsr = 16'hACE1;

    always @(posedge clk_25mhz) begin
        // Continuously run LFSR to ensure entropy based on human timing
        lfsr <= {lfsr[14:0], lfsr[15] ^ lfsr[13] ^ lfsr[12] ^ lfsr[10]};
    end

    // --- Main FSM ---
    reg [24:0] blink_counter = 0; // For visual effects in IDLE/FAULT

    always @(posedge clk_25mhz) begin
        blink_counter <= blink_counter + 1;

        // Millisecond tick generator
        if (ms_counter >= MS_TICKS - 1) begin
            ms_counter <= 0;
            if (state == S_WAIT || state == S_TEST) begin
                timer_ms <= timer_ms + 1;
            end
        end else begin
            ms_counter <= ms_counter + 1;
        end

        case (state)
            S_IDLE: begin
                // Alternating outer LEDs to show IDLE
                led <= blink_counter[23] ? 8'b10000001 : 8'b01000010;
                if (start_pressed) begin
                    // Calculate wait time: 1000 ms + random (0 to 2047 ms)
                    random_wait_ms <= 1000 + (lfsr & 16'h07FF);
                    timer_ms <= 0;
                    ms_counter <= 0;
                    state <= S_WAIT;
                end
            end

            S_WAIT: begin
                led <= 8'b00000000; // All LEDs off during wait
                if (react_pressed) begin
                    // False start!
                    state <= S_FAULT;
                end else if (timer_ms >= random_wait_ms) begin
                    timer_ms <= 0;
                    state <= S_TEST;
                end
            end

            S_TEST: begin
                led <= 8'b11111111; // GO signal!
                if (react_pressed) begin
                    state <= S_RESULT;
                end else if (timer_ms > 5000) begin
                    // Timeout after 5 seconds
                    state <= S_IDLE;
                end
            end

            S_RESULT: begin
                // Display bar graph based on reaction time (timer_ms)
                // <150ms: 1 LED, 150-199: 2 LEDs, 200-249: 3 LEDs... >450ms: 8 LEDs
                if (timer_ms < 150)       led <= 8'b00000001;
                else if (timer_ms < 200)  led <= 8'b00000011;
                else if (timer_ms < 250)  led <= 8'b00000111;
                else if (timer_ms < 300)  led <= 8'b00001111;
                else if (timer_ms < 350)  led <= 8'b00011111;
                else if (timer_ms < 400)  led <= 8'b00111111;
                else if (timer_ms < 450)  led <= 8'b01111111;
                else                      led <= 8'b11111111;

                if (start_pressed) begin
                    state <= S_IDLE;
                end
            end

            S_FAULT: begin
                // Fast blink all LEDs to indicate false start
                led <= blink_counter[22] ? 8'b11111111 : 8'b00000000;
                if (start_pressed) begin
                    state <= S_IDLE;
                end
            end

            default: state <= S_IDLE;
        endcase
    end

endmodule

2. Banco de pruebas Verilog: reaction_trainer_tb.v

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

`timescale 1ns/1ps

module reaction_trainer_tb;

    reg clk;
    reg btn_start;
    reg btn_react;
    wire [7:0] led;

    // Instantiate the Device Under Test (DUT)
    // Override parameters to speed up simulation drastically
    reaction_trainer #(
        .MS_TICKS(2),      // 2 ticks = 1 "ms" for simulation
        .DEBOUNCE_TICKS(5) // 5 ticks for debounce
    ) dut (
        .clk_25mhz(clk),
        .btn_start(btn_start),
        .btn_react(btn_react),
        .led(led)
    );

    // Generate simulated clock
    initial begin
        clk = 0;
        forever #20 clk = ~clk; // 40ns period -> 25MHz
    end

    initial begin
        $dumpfile("reaction_trainer.vcd");
        $dumpvars(0, reaction_trainer_tb);

        // Initialize inputs
// ...

`timescale 1ns/1ps

module reaction_trainer_tb;

    reg clk;
    reg btn_start;
    reg btn_react;
    wire [7:0] led;

    // Instantiate the Device Under Test (DUT)
    // Override parameters to speed up simulation drastically
    reaction_trainer #(
        .MS_TICKS(2),      // 2 ticks = 1 "ms" for simulation
        .DEBOUNCE_TICKS(5) // 5 ticks for debounce
    ) dut (
        .clk_25mhz(clk),
        .btn_start(btn_start),
        .btn_react(btn_react),
        .led(led)
    );

    // Generate simulated clock
    initial begin
        clk = 0;
        forever #20 clk = ~clk; // 40ns period -> 25MHz
    end

    initial begin
        $dumpfile("reaction_trainer.vcd");
        $dumpvars(0, reaction_trainer_tb);

        // Initialize inputs
        btn_start = 0;
        btn_react = 0;

        // Wait a few cycles in IDLE
        #1000;

        // Press START button
        $display("[%0t] Pressing START button...", $time);
        btn_start = 1;
        #500;
        btn_start = 0;

        // Wait for LED to turn on (TEST state)
        $display("[%0t] Waiting for GO signal...", $time);
        wait(led == 8'b11111111);
        $display("[%0t] LED is ON! Reacting...", $time);

        // Simulate a fast reaction time (e.g., 180ms)
        // In this TB, 1ms = 2 ticks = 80ns. So 180ms = 14400ns.
        #14400;
        btn_react = 1;
        #500;
        btn_react = 0;

        // Wait for RESULT state to settle
        #5000;
        $display("[%0t] Simulation complete. LED output: %b", $time, led);
        $finish;
    end

endmodule

3. Restricciones de la placa: ulx3s.lpf

LOCATE COMP "clk_25mhz" SITE "G2";
IOBUF PORT "clk_25mhz" PULLMODE=NONE IO_TYPE=LVCMOS33;

LOCATE COMP "btn_start" SITE "R1";
IOBUF PORT "btn_start" PULLMODE=DOWN IO_TYPE=LVCMOS33;

LOCATE COMP "btn_react" SITE "T1";
IOBUF PORT "btn_react" PULLMODE=DOWN IO_TYPE=LVCMOS33;

LOCATE COMP "led[0]" SITE "B2";
LOCATE COMP "led[1]" SITE "C2";
LOCATE COMP "led[2]" SITE "C1";
LOCATE COMP "led[3]" SITE "D2";
LOCATE COMP "led[4]" SITE "D1";
LOCATE COMP "led[5]" SITE "E2";
LOCATE COMP "led[6]" SITE "E1";
LOCATE COMP "led[7]" SITE "H3";

IOBUF PORT "led[0]" PULLMODE=NONE IO_TYPE=LVCMOS33;
IOBUF PORT "led[1]" PULLMODE=NONE IO_TYPE=LVCMOS33;
IOBUF PORT "led[2]" PULLMODE=NONE IO_TYPE=LVCMOS33;
IOBUF PORT "led[3]" PULLMODE=NONE IO_TYPE=LVCMOS33;
IOBUF PORT "led[4]" PULLMODE=NONE IO_TYPE=LVCMOS33;
IOBUF PORT "led[5]" PULLMODE=NONE IO_TYPE=LVCMOS33;
IOBUF PORT "led[6]" PULLMODE=NONE IO_TYPE=LVCMOS33;
IOBUF PORT "led[7]" PULLMODE=NONE IO_TYPE=LVCMOS33;

4. Script de compilación: build.sh

#!/bin/bash
set -e

echo "Linting and Simulating with Verilator..."
verilator --lint-only reaction_trainer.v

echo "Synthesizing with Yosys..."
yosys -p "synth_ecp5 -json reaction_trainer.json" reaction_trainer.v

echo "Place and Route with nextpnr..."
nextpnr-ecp5 --85k --json reaction_trainer.json --lpf ulx3s.lpf --textcfg reaction_trainer_out.config

echo "Packing bitstream..."
ecppack reaction_trainer_out.config reaction_trainer.bit

echo "Build complete. To flash, run:"
echo "openFPGALoader --board=ulx3s reaction_trainer.bit"

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Qué tipo de sistema se va a construir según el artículo?




Pregunta 2: ¿Qué componente se utiliza para generar retardos aleatorios en el sistema?




Pregunta 3: ¿Cuál es el propósito principal del temporizador de hardware en este proyecto?




Pregunta 4: ¿Para qué sirve este proyecto en el ámbito del prototipado de hardware?




Pregunta 5: En las pruebas de 'humanos en el bucle', ¿qué demuestra esta herramienta educativa?




Pregunta 6: ¿Qué ventaja ofrece este sistema frente a los temporizadores basados en software?




Pregunta 7: ¿Cuál es la precisión afirmada del temporizador de hardware?




Pregunta 8: ¿Qué herramienta se utiliza para validar la afirmación de precisión del sistema?




Pregunta 9: ¿Qué programa se menciona para inspeccionar la forma de onda VCD resultante?




Pregunta 10: ¿A qué equivale el delta exacto de 40 ns mostrado en la validación?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:


Caso práctico: alarma watchdog de pulsos con ULX3S

Alarma watchdog de pulsos con ULX3S

Objetivo y caso de uso

Qué construirás: Una alarma de perro guardián (watchdog) de pérdida de pulso basada en hardware que monitorea continuamente una señal periódica de latido (heartbeat). Activa un zumbador piezoeléctrico audible y un LED visual con latencia determinista de submilisegundos si la señal se interrumpe dentro de un período de tiempo predefinido.

Por qué es importante / Casos de uso

  • Supervisión de sistemas embebidos: Monitorea una onda cuadrada de «latido» de una MCU, alertando instantáneamente a los operadores si el procesador falla o entra en un bucle infinito.
  • Automatización industrial: Detecta cintas transportadoras atascadas o maquinaria bloqueada monitoreando los pulsos de codificadores ópticos o magnéticos en busca de paradas inesperadas.
  • Monitoreo de seguridad: Valida la integridad física de la línea, detectando inmediatamente cables cortados o cables de sensores desconectados cuando un pulso continuo se silencia.

Resultado esperado

  • Un temporizador watchdog sintetizado en FPGA con detección de fallos precisa por ciclo (cycle-accurate).
  • Activación inmediata de GPIO de un LED y un zumbador piezoeléctrico ante la pérdida de pulso.
  • Conciencia de seguridad: Comprender que esta implementación educativa debe ser reemplazada por hardware de seguridad a prueba de fallos (fail-safe) certificado y redundante en entornos de producción reales.

Audiencia: Ingenieros de sistemas embebidos, Desarrolladores de FPGA; Nivel: Intermedio

Arquitectura/flujo: Un flanco de señal entrante reinicia un contador descendente de hardware interno de la FPGA. Si el contador llega a cero (por ejemplo, han transcurrido >50ms sin un pulso), un latch de alarma activa los GPIO de salida para accionar el zumbador y el LED.

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. Para este perfil FPGA/ULX3S, los bloques de Verilog sintetizable se comprobaron con Yosys (read_verilog) y el conjunto Verilog de diseño/test se revisó con Verilator. El validador también comprobó la estructura de los bloques de código, que los comandos usen opciones copiables con guiones ASCII, que no aparezcan stacks no soportados y que esté disponible la toolchain ULX3S/ECP5 (yosys, nextpnr-ecp5, ecppack, openFPGALoader).

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 1 tablas y 4 bloques de código detectados antes de publicar.
  • Código comprobado: 2 Verilog/Yosys-Verilator, 1 Bash/copy-paste checks.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma compatibilidad sintáctica y de herramientas para el código publicado, pero no sustituye la prueba física sobre tu revisión exacta de ULX3S, tu archivo de restricciones de pines y tu cableado real.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender la configuración, verifica el mapa de pines (pinout) de tu revisión exacta de la placa ULX3S, mantén las señales de E/S de la FPGA a 3.3 V, nunca conectes 5 V directamente a los pines de E/S, desconecta la alimentación antes de cambiar el cableado y utiliza fuentes externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.

Requisitos previos

Para completar con éxito este tutorial, necesitas:
* Comprensión básica de lógica digital (flip-flops, contadores y dominios de reloj).
* Una instalación funcional de la cadena de herramientas de código abierto para FPGA Lattice ECP5:
* Verilator (versión 5.0 o posterior recomendada para soporte de --binary) para análisis estático (linting) y simulación.
* Yosys para síntesis lógica.
* nextpnr-ecp5 para ubicación y enrutamiento (place and route).
* Project Trellis (ecppack) para la generación del bitstream.
* openFPGALoader para programar el dispositivo.
* Un editor de texto para crear archivos fuente Verilog, bancos de pruebas (testbenches) y archivos de restricciones.
* Familiaridad básica con el uso de una interfaz de línea de comandos (CLI) para ejecutar comandos de compilación.

Materiales

  • Placa de desarrollo FPGA Radiona ULX3S (Lattice ECP5-85F).
  • Cable puente (jumper) para entrada de pulso: Un cable puente Dupont estándar macho a macho (usado para simular pulsos manualmente tocando un pin de 3.3V, o para conectar un generador de señales externo).
  • Zumbador piezoeléctrico: Un módulo de zumbador (buzzer) piezoeléctrico pasivo estándar (capaz de producir sonido cuando es accionado por una onda cuadrada).
  • LED de estado: Usaremos uno de los LED rojos integrados provistos en la placa ULX3S.
  • Protoboard y cables puente adicionales (opcional, para asegurar el zumbador piezoeléctrico).

Configuración/Conexión

El Radiona ULX3S proporciona conectores GPIO versátiles (J1 y J2) que operan a niveles lógicos de 3.3V. Usaremos el conector J1 para nuestras conexiones externas.

Componente / Función Conexión física Pin de la FPGA Estándar de E/S Notas
Reloj del sistema Oscilador integrado de 25 MHz G2 LVCMOS33 Acciona toda la lógica síncrona.
Entrada de pulso Conector J1, Pin gp[0] B11 LVCMOS33 Con resistencia pull-down interna (pulled DOWN). Conecta un puente entre 3.3V y este pin para simular un pulso.
Zumbador piezoeléctrico Conector J1, Pin gp[1] A10 LVCMOS33 Conecta el positivo del zumbador a gp[1] y el negativo a GND en el conector J1.
LED de estado LED 0 integrado B2 LVCMOS33 Se ilumina cuando el watchdog agota el tiempo (timeout).

Instrucciones de cableado:
1. Localiza el conector J1 en la ULX3S.
2. Conecta la pata positiva de tu zumbador piezoeléctrico pasivo al pin etiquetado gp[1] (pin A10 de la FPGA).
3. Conecta la pata negativa del zumbador piezoeléctrico a un pin GND en la ULX3S.
4. Toma tu cable puente de entrada de pulso y conecta un extremo en el pin etiquetado gp[0] (pin B11 de la FPGA). Deja el otro extremo suelto por ahora; lo tocarás contra un pin de 3.3V en el conector J1 para simular los pulsos de latido (heartbeat) entrantes.

Código validado

El proyecto consta de tres archivos: el módulo Verilog principal sintetizable, el banco de pruebas (testbench) Verilog para simulación y el Archivo de Preferencias Lógicas (LPF) para restricciones de pines. Guarda estos archivos en el mismo directorio de trabajo.

1. Módulo principal (watchdog.v)

Este módulo sincroniza la entrada asíncrona, detecta flancos de subida, cuenta los ciclos de reloj para medir el tiempo de espera (timeout) y genera un tono de 2 kHz si se alcanza dicho tiempo.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/*
 * Module: watchdog
 * Description: Missing-pulse watchdog alarm. Triggers an LED and a 2 kHz 
 * piezo buzzer tone if a rising edge is not detected within the timeout period.
 */
module watchdog #(
    // Defaults for 25 MHz clock
    parameter TIMEOUT_CYCLES = 25000000, // 1 second timeout
    parameter TONE_CYCLES    = 12500     // 2 kHz tone period (25M / 2k)
)(
    input  wire clk_25mhz,
    input  wire pulse_in,
    output wire buzzer_out,
    output wire led_out
);

    // --------------------------------------------------------
    // 1. Metastability Synchronizer
    // --------------------------------------------------------
    // The input pulse comes from the outside world and is asynchronous.
    // We use a 2-stage shift register to synchronize it to the 25MHz clock.
    reg [1:0] sync_reg = 2'b00;

    always @(posedge clk_25mhz) begin
        sync_reg <= {sync_reg[0], pulse_in};
    end

    // --------------------------------------------------------
    // 2. Edge Detector
    // --------------------------------------------------------
    // Compare the current synchronized state with the previous state
    // to generate a single-cycle pulse on the rising edge.
    reg pulse_prev = 1'b0;
    wire pulse_edge;

    always @(posedge clk_25mhz) begin
        pulse_prev <= sync_reg[1];
    end

    assign pulse_edge = (sync_reg[1] && !pulse_prev);

    // --------------------------------------------------------
    // 3. Watchdog Timer
    // --------------------------------------------------------
    reg [24:0] timer_cnt = 25'd0;
// ...

/*
 * Module: watchdog
 * Description: Missing-pulse watchdog alarm. Triggers an LED and a 2 kHz 
 * piezo buzzer tone if a rising edge is not detected within the timeout period.
 */
module watchdog #(
    // Defaults for 25 MHz clock
    parameter TIMEOUT_CYCLES = 25000000, // 1 second timeout
    parameter TONE_CYCLES    = 12500     // 2 kHz tone period (25M / 2k)
)(
    input  wire clk_25mhz,
    input  wire pulse_in,
    output wire buzzer_out,
    output wire led_out
);

    // --------------------------------------------------------
    // 1. Metastability Synchronizer
    // --------------------------------------------------------
    // The input pulse comes from the outside world and is asynchronous.
    // We use a 2-stage shift register to synchronize it to the 25MHz clock.
    reg [1:0] sync_reg = 2'b00;

    always @(posedge clk_25mhz) begin
        sync_reg <= {sync_reg[0], pulse_in};
    end

    // --------------------------------------------------------
    // 2. Edge Detector
    // --------------------------------------------------------
    // Compare the current synchronized state with the previous state
    // to generate a single-cycle pulse on the rising edge.
    reg pulse_prev = 1'b0;
    wire pulse_edge;

    always @(posedge clk_25mhz) begin
        pulse_prev <= sync_reg[1];
    end

    assign pulse_edge = (sync_reg[1] && !pulse_prev);

    // --------------------------------------------------------
    // 3. Watchdog Timer
    // --------------------------------------------------------
    reg [24:0] timer_cnt = 25'd0;
    reg alarm_state = 1'b0;

    always @(posedge clk_25mhz) begin
        if (pulse_edge) begin
            // Valid pulse received: reset timer and clear alarm
            timer_cnt <= 25'd0;
            alarm_state <= 1'b0;
        end else if (timer_cnt < TIMEOUT_CYCLES) begin
            // No pulse, but timeout not yet reached: increment timer
            timer_cnt <= timer_cnt + 1'b1;
        end else begin
            // Timeout reached: trigger alarm
            alarm_state <= 1'b1;
        end
    end

    // --------------------------------------------------------
    // 4. Tone Generator (2 kHz Square Wave)
    // --------------------------------------------------------
    reg [13:0] tone_cnt = 14'd0;
    reg tone_out = 1'b0;

    always @(posedge clk_25mhz) begin
        if (alarm_state) begin
            // Toggle the buzzer state every half-period
            if (tone_cnt >= (TONE_CYCLES / 2) - 1) begin
                tone_cnt <= 14'd0;
                tone_out <= ~tone_out;
            end else begin
                tone_cnt <= tone_cnt + 1'b1;
            end
        end else begin
            // Ensure buzzer is silent when alarm is inactive
            tone_cnt <= 14'd0;
            tone_out <= 1'b0;
        end
    end

    // --------------------------------------------------------
    // 5. Output Assignments
    // --------------------------------------------------------
    assign buzzer_out = tone_out;
    assign led_out    = alarm_state;

endmodule

2. Banco de pruebas (watchdog_tb.v)

El banco de pruebas sobrescribe los parámetros de tiempo predeterminados para acelerar la simulación. Simular 25 millones de ciclos sería computacionalmente costoso e innecesario para verificar la lógica. El banco de pruebas comprueba automáticamente si la salida se activa y se restablece correctamente.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/*
 * Module: watchdog_tb
 * Description: Simulation testbench for the missing-pulse watchdog.
 */
`timescale 1ns/1ps

module watchdog_tb;

    reg clk;
    reg pulse_in;
    wire buzzer_out;
    wire led_out;

    // Instantiate the Device Under Test (DUT)
    // Override parameters for faster simulation:
    // Timeout = 100 cycles, Tone period = 10 cycles
    watchdog #(
        .TIMEOUT_CYCLES(100),
        .TONE_CYCLES(10)
    ) dut (
        .clk_25mhz(clk),
        .pulse_in(pulse_in),
        .buzzer_out(buzzer_out),
        .led_out(led_out)
    );

    // Clock generation (25 MHz = 40ns period)
    initial clk = 0;
    always #20 clk = ~clk;

    initial begin
        $dumpfile("watchdog_tb.vcd");
        $dumpvars(0, watchdog_tb);

        // Initialize inputs
        pulse_in = 0;
// ...

/*
 * Module: watchdog_tb
 * Description: Simulation testbench for the missing-pulse watchdog.
 */
`timescale 1ns/1ps

module watchdog_tb;

    reg clk;
    reg pulse_in;
    wire buzzer_out;
    wire led_out;

    // Instantiate the Device Under Test (DUT)
    // Override parameters for faster simulation:
    // Timeout = 100 cycles, Tone period = 10 cycles
    watchdog #(
        .TIMEOUT_CYCLES(100),
        .TONE_CYCLES(10)
    ) dut (
        .clk_25mhz(clk),
        .pulse_in(pulse_in),
        .buzzer_out(buzzer_out),
        .led_out(led_out)
    );

    // Clock generation (25 MHz = 40ns period)
    initial clk = 0;
    always #20 clk = ~clk;

    initial begin
        $dumpfile("watchdog_tb.vcd");
        $dumpvars(0, watchdog_tb);

        // Initialize inputs
        pulse_in = 0;

        // Wait for reset/initialization
        #100;

        // Apply a pulse
        pulse_in = 1;
        #40;
        pulse_in = 0;

        // Wait for timeout (100 cycles * 40ns = 4000ns)
        #5000;

        if (led_out !== 1'b1) begin
            $display("FAIL: Alarm did not trigger after timeout.");
        end else begin
            $display("PASS: Alarm triggered successfully.");
        end

        // Apply another pulse to reset alarm
        pulse_in = 1;
        #40;
        pulse_in = 0;

        #100;
        if (led_out !== 1'b0) begin
            $display("FAIL: Alarm did not clear after new pulse.");
        end else begin
            $display("PASS: Alarm cleared successfully.");
        end

        #1000;
        $finish;
    end

endmodule

3. Archivo de restricciones (ulx3s.lpf)

Este archivo mapea los puertos Verilog de nivel superior a los pines físicos de la placa Radiona ULX3S ECP5-85F.

LOCATE COMP "clk_25mhz" SITE "G2";
IOBUF COMP "clk_25mhz" IO_TYPE=LVCMOS33;

LOCATE COMP "pulse_in" SITE "B11";
IOBUF COMP "pulse_in" IO_TYPE=LVCMOS33 PULLMODE=DOWN;

LOCATE COMP "buzzer_out" SITE "A10";
IOBUF COMP "buzzer_out" IO_TYPE=LVCMOS33;

LOCATE COMP "led_out" SITE "B2";
IOBUF COMP "led_out" IO_TYPE=LVCMOS33;

Compilación y validación

Ejecuta los siguientes comandos en tu terminal para simular la lógica, sintetizar el diseño y programar la placa FPGA ULX3S.

# 1. Lint and simulate with Verilator to validate logic
verilator --binary --trace watchdog.v watchdog_tb.v
./obj_dir/Vwatchdog_tb

# 2. Synthesize with Yosys
yosys -p "synth_ecp5 -top watchdog -json watchdog.json" watchdog.v

# 3. Place and Route with nextpnr
nextpnr-ecp5 --85k --package CABGA381 --json watchdog.json --lpf ulx3s.lpf --textcfg watchdog.config

# 4. Pack bitstream
ecppack watchdog.config watchdog.bit

# 5. Program the ULX3S via openFPGALoader
openFPGALoader --board ulx3s watchdog.bit

Evidencia de validación esperada:
1. Fase de simulación: La ejecución de Verilator (./obj_dir/Vwatchdog_tb) debería imprimir PASS: Alarm triggered successfully. seguido de PASS: Alarm cleared successfully. en la consola, confirmando un recuento de ciclos preciso.
2. Fase de hardware: Al flashear, el LED rojo se encenderá inmediatamente y el zumbador emitirá un tono de 2 kHz porque no llegan pulsos.
3. Prueba manual: Toca con el cable puente suelto el pin de 3.3V en el conector J1. El LED y el zumbador se apagarán al instante. Si dejas de tocar, exactamente 1.0 segundos después, la alarma se reanudará.

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Qué tipo de dispositivo se construye en el proyecto descrito?




Pregunta 2: ¿Qué tipo de señal monitorea continuamente la alarma?




Pregunta 3: ¿Qué componentes se activan si la señal se interrumpe?




Pregunta 4: ¿Cuál es la latencia de activación de la alarma al detectar la interrupción?




Pregunta 5: En la supervisión de sistemas embebidos, ¿qué indica la interrupción de la onda cuadrada de 'latido'?




Pregunta 6: ¿Cómo se utiliza este sistema en la automatización industrial?




Pregunta 7: En el contexto de monitoreo de seguridad, ¿qué permite detectar este sistema?




Pregunta 8: ¿En qué tecnología se sintetiza el temporizador watchdog según el resultado esperado?




Pregunta 9: ¿Qué nivel de precisión tiene la detección de fallos del temporizador watchdog sintetizado?




Pregunta 10: ¿Qué tipo de activación se produce en el LED y el zumbador piezoeléctrico?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme: