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
Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.
Ruta de validación
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
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.




