Objetivo y caso de uso
Qué construirás: Implementar un parpadeo estable de un LED en la placa Basys 3 FPGA utilizando un divisor de reloj digital.
Para qué sirve
- Controlar la frecuencia de parpadeo de LEDs en proyectos de visualización.
- Demostrar el uso de divisores de frecuencia en sistemas digitales.
- Validar el funcionamiento de la FPGA Basys 3 en aplicaciones prácticas.
Resultado esperado
- LED LD0 parpadeando a 1 Hz, observable en hardware real.
- Medición de latencia de respuesta del sistema inferior a 10 ms.
- Consumo de recursos FPGA: menos del 5% de la lógica utilizada.
Público objetivo: Estudiantes y entusiastas de la electrónica; Nivel: Básico
Arquitectura/flujo: Diseño en Verilog, implementación en Vivado, validación en Basys 3.
Nivel: Básico
Objetivo del proyecto
Implementar en la FPGA Basys 3 (Xilinx Artix‑7) un parpadeo estable de un LED usando el reloj interno de 100 MHz y un divisor de frecuencia puramente digital. El diseño “led-blink-with-clock-divider” se compone de:
– Un módulo de división de reloj por conteo.
– Un top que conecta el reloj de 100 MHz (CLK100MHZ) a ese divisor.
– Un único LED de usuario (LD0) que cambia de estado una vez por segundo (1 Hz).
El flujo completo abarca desde la instalación de la toolchain hasta la validación en hardware real, con comandos reproducibles (CLI) y ficheros de proyecto (Verilog/XDC/Tcl) coherentes con Basys 3.
Prerrequisitos
Sistema operativo soportado y herramientas exactas
- Windows 11 Pro 22H2 (64-bit) o Windows 10 Pro 22H2 (64-bit).
- Ubuntu 22.04.4 LTS (64-bit).
Toolchain exacta (Xilinx)
- Xilinx Vivado Design Suite 2023.2 (WebPACK Edition).
- Versión: 2023.2 (64-bit)
- Incluye soporte para Artix‑7 y el cable JTAG Digilent.
- Ruta típica:
- Windows: C:\Xilinx\Vivado\2023.2\bin\vivado.bat
- Linux: /opt/Xilinx/Vivado/2023.2/bin/vivado
- Cable/driver Digilent para JTAG:
- Instalado junto con Vivado 2023.2. En Windows, verificar el “Xilinx USB Cable Driver” y el “Digilent Adept Runtime” que vienen con el instalador de Vivado (opción por defecto).
Conocimientos previos mínimos
- Conocer la diferencia entre síntesis, implementación (place & route) y generación de bitstream en FPGA.
- Lectura básica de Verilog (combinacional y secuencial con always @(posedge clk)).
- Manejo básico de la línea de comandos en Windows PowerShell o Linux Bash.
Materiales
- 1 x FPGA Board: Basys 3 (Xilinx Artix‑7), modelo exacto: Basys 3 (Artix‑7 XC7A35T-1CPG236C).
- 1 x Cable micro‑USB de buena calidad.
- 1 x PC con Windows 11/10 o Ubuntu 22.04, con Vivado 2023.2 WebPACK instalado.
- Opcional: Multímetro/cronómetro (para validar tiempos aproximados del parpadeo).
Nota: Mantén el switch de alimentación de la Basys 3 en OFF mientras conectas el cable USB. La Basys 3 suele alimentarse por el mismo micro‑USB.
Preparación y conexión
Pasos previos
- Instala Vivado 2023.2 (WebPACK).
- Durante la instalación, deja activadas las opciones de “Vivado Design Suite (WebPACK)” y los drivers USB/JTAG.
- Reinicia el sistema si lo solicita el instalador (Windows).
- Verifica que la ruta al binario de Vivado está accesible:
- Windows PowerShell:
- C:\Xilinx\Vivado\2023.2\bin\vivado.bat -version
- Linux:
- /opt/Xilinx/Vivado/2023.2/bin/vivado -version
- Descarga/crea una carpeta de trabajo limpia (por ejemplo, %HOMEPATH%\fpga-basys3-blink en Windows o ~/fpga-basys3-blink en Linux).
Conexión física de la placa
- Conecta el cable micro‑USB al conector USB PROG/UART de la Basys 3 (el único puerto micro‑USB de la placa).
- Conecta el otro extremo al PC.
- Pon el interruptor de encendido (SW) de la Basys 3 en ON.
- Confirma que el LED de alimentación (POWER) en la placa está encendido.
Asignación de puertos y pines (Basys 3)
Para este caso práctico, usaremos exclusivamente:
– El reloj de 100 MHz de la placa (CLK100MHZ).
– El LED de usuario LD0.
Tabla de mapeo de puertos-lógicos a pines físicos y estándares eléctricos (XDC):
| Señal lógica | Pin físico | Banco | Función | IOSTANDARD | Comentario |
|---|---|---|---|---|---|
| CLK100MHZ | W5 | 35 | Reloj 100 MHz (oscil.) | LVCMOS33 | Reloj de sistema de la Basys 3 |
| led | U16 | 35 | LED de usuario LD0 | LVCMOS33 | LED 0 (encendido = ‘1’) |
Notas:
– El reloj de 100 MHz interno está cableado a la FPGA en el pin W5.
– El LED LD0 se mapea en U16. Ambos pertenecen a banco alimentado a 3.3 V (LVCMOS33).
– El diseño usará exactamente estos nombres de puertos lógicos: CLK100MHZ y led.
Código completo (Verilog + XDC) con explicación
Estructura de archivos
- src/top.v: módulo top con divisor de reloj y salida al LED.
- constraints/basys3_blink.xdc: restricciones de pines y definición del reloj.
- scripts/build.tcl: script TCL para proyecto, síntesis, implementación y bitstream.
Verilog del diseño (src/top.v)
Este diseño divide el reloj de 100 MHz para producir un parpadeo de aproximadamente 1 Hz en el LED LD0. Se cuenta hasta 49,999,999 ciclos; al alcanzar ese valor se invierte el estado del LED y se reinicia el contador, resultando en un toggle cada 0.5 s (parpadeo de 1 s periodo completo: ON 0.5 s, OFF 0.5 s).
// File: src/top.v
// Target: Basys 3 (Xilinx Artix-7 XC7A35T-1CPG236C)
// Tool: Vivado 2023.2 (WebPACK Edition)
// Función: Divisor de reloj por conteo para parpadeo 1 Hz en LED LD0
module top (
input wire CLK100MHZ, // Reloj de 100 MHz de la Basys 3
output reg led // LED de usuario LD0
);
// Objetivo: toggle cada 0.5 s => 100e6 * 0.5 = 50_000_000 ciclos
localparam integer HALF_PERIOD_TICKS = 50_000_000 - 1; // 0..49_999_999
localparam integer CNT_WIDTH = 26; // ceil(log2(50e6)) = 26
reg [CNT_WIDTH-1:0] cnt = {CNT_WIDTH{1'b0}};
always @(posedge CLK100MHZ) begin
if (cnt == HALF_PERIOD_TICKS[CNT_WIDTH-1:0]) begin
cnt <= {CNT_WIDTH{1'b0}};
led <= ~led;
end else begin
cnt <= cnt + 1'b1;
end
end
endmodule
Puntos clave:
– No se usan recursos analógicos o PLLs/MMCM; es un divisor por conteo síncrono puro.
– CNT_WIDTH=26 es suficiente para contar hasta 50 millones con valores 0..49_999_999.
– led es un registro (reg) que se conmuta cuando el contador alcanza el final.
Restricciones XDC (constraints/basys3_blink.xdc)
Este archivo mapea puertos lógicos a pines físicos y define el reloj para el análisis de temporización:
# File: constraints/basys3_blink.xdc
# Basys 3 (Artix-7 XC7A35T-1CPG236C) - LED0 y reloj 100 MHz
## Reloj 100 MHz en pin W5
set_property -dict { PACKAGE_PIN W5 IOSTANDARD LVCMOS33 } [get_ports { CLK100MHZ }]
create_clock -add -name sys_clk -period 10.000 [get_ports { CLK100MHZ }]
## LED de usuario LD0 en U16
set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports { led }]
set_property DRIVE 8 [get_ports { led }]
set_property SLEW SLOW [get_ports { led }]
Puntos clave:
– create_clock con período de 10 ns (100 MHz).
– IOSTANDARD LVCMOS33 apropiado para los bancos de usuario.
– Ajuste de DRIVE y SLEW conservador para minimizar ruido.
Compilación, programación y ejecución (CLI exacta con Vivado 2023.2)
A continuación se dan las rutas y comandos exactamente reproducibles. Se muestran tanto para Linux como para Windows. Ajusta la ruta base de tu proyecto.
Estructura de carpetas recomendada:
– fpga-basys3-blink/
– src/top.v
– constraints/basys3_blink.xdc
– scripts/build.tcl
– scripts/program.tcl (opcional si separas la programación)
– vivado/ (se generará el proyecto y los productos de la compilación)
Script Tcl para construir el proyecto y generar el bitstream (scripts/build.tcl)
Este script crea el proyecto, añade fuentes, sintetiza, implementa y genera el bitstream en modo batch.
# File: scripts/build.tcl
# Vivado 2023.2 (WebPACK)
# Proyecto: basys3_blink
# Parte: xc7a35tcpg236-1
# 1) Configuración de rutas relativas
set proj_name "basys3_blink"
set proj_dir [file normalize "../vivado/$proj_name"]
set src_dir [file normalize "../src"]
set xdc_dir [file normalize "../constraints"]
# 2) Crear proyecto nuevo (forzar si existe)
create_project $proj_name $proj_dir -part xc7a35tcpg236-1 -force
# 3) Añadir fuentes HDL
add_files -fileset sources_1 [list \
[file join $src_dir "top.v"] \
]
# 4) Añadir fichero de restricciones XDC
add_files -fileset constrs_1 [list \
[file join $xdc_dir "basys3_blink.xdc"] \
]
# 5) Establecer el top
set_property top top [current_fileset]
# 6) Actualizar orden de compilación
update_compile_order -fileset sources_1
# 7) Ejecutar síntesis, implementación y bitstream
launch_runs synth_1 -jobs 4
wait_on_run synth_1
launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1
# 8) Reportes útiles
open_run impl_1
report_timing_summary -file [file join $proj_dir "timing_summary_impl_1.rpt"] -delay_type max -max_paths 10
report_utilization -file [file join $proj_dir "utilization_impl_1.rpt"] -hierarchical -hierarchical_depth 2
# 9) Imprimir ruta del bitstream
set bitfile [file join $proj_dir "$proj_name.runs/impl_1/top.bit"]
puts "BITSTREAM_GENERATED: $bitfile"
Script Tcl para programar la FPGA (scripts/program.tcl)
Puedes separar la programación del flujo de build si lo prefieres:
# File: scripts/program.tcl
# Programación por JTAG con Vivado 2023.2
set proj_name "basys3_blink"
set proj_dir [file normalize "../vivado/$proj_name"]
set bitfile [file join $proj_dir "$proj_name.runs/impl_1/top.bit"]
open_hw
connect_hw_server
open_hw_target
# Seleccionar primer dispositivo Artix-7 conectado
set devs [get_hw_devices *]
if {[llength $devs] == 0} {
puts "ERROR: No se detectan dispositivos JTAG."; exit 1
}
current_hw_device [lindex $devs 0]
# Asignar bitstream y programar
set_property PROGRAM.FILE $bitfile [current_hw_device]
program_hw_devices [current_hw_device]
refresh_hw_device [current_hw_device]
puts "PROGRAM_DONE: $bitfile"
Ejecución de los scripts (Linux)
- Crea la estructura y copia los archivos tal como se indicó.
- Ejecuta build:
- /opt/Xilinx/Vivado/2023.2/bin/vivado -mode batch -source scripts/build.tcl
- Programa la FPGA (con la Basys 3 encendida y conectada por USB):
- /opt/Xilinx/Vivado/2023.2/bin/vivado -mode batch -source scripts/program.tcl
Ejecución de los scripts (Windows PowerShell)
- Abre PowerShell en la carpeta del proyecto.
- Ejecuta build:
- & «C:\Xilinx\Vivado\2023.2\bin\vivado.bat» -mode batch -source scripts\build.tcl
- Programa la FPGA:
- & «C:\Xilinx\Vivado\2023.2\bin\vivado.bat» -mode batch -source scripts\program.tcl
Comprobación rápida:
– La salida de build.tcl debe contener “BITSTREAM_GENERATED: …/top.bit”.
– La salida de program.tcl debe terminar con “PROGRAM_DONE: …/top.bit”.
Validación paso a paso
- Observación visual:
- El LED LD0 de la Basys 3 debe parpadear con un ciclo de aproximadamente 1 segundo (0.5 s ON, 0.5 s OFF).
-
Si el LED permanece apagado o encendido, revisar los apartados de troubleshooting.
-
Medición con cronómetro:
- Cronometra 10 parpadeos completos; el tiempo total debería ser cercano a 10 segundos.
-
Acepta pequeñas variaciones por el conteo humano y latencias no relevantes.
-
Verificación de opciones de temporización:
- Abre el reporte “timing_summary_impl_1.rpt” en vivado/basys3_blink/.
-
Confirma que no hay fallos de temporización (WNS >= 0). Para este diseño sencillo, no deberían existir violaciones.
-
Confirmación del mapeo de pines:
-
En Vivado GUI (opcional), abre el proyecto generado, ve a I/O Planning y confirma:
- CLK100MHZ asignado a W5, LVCMOS33.
- led asignado a U16, LVCMOS33.
-
Persistencia del estado:
- El parpadeo debe mantenerse mientras la placa permanece encendida.
- Al reprogramar con program.tcl, el LED debería entrar en parpadeo inmediatamente tras la configuración.
Troubleshooting (errores típicos y solución)
- No se detecta la placa en program.tcl (get_hw_devices vacío):
- Verifica que la Basys 3 está encendida y conectada por USB.
- Prueba con otro puerto USB o cable micro‑USB.
- Windows: revisa Administrador de dispositivos → Universal Serial Bus devices → “Digilent USB Device” o similar.
-
Reinstala drivers USB/Xilinx Cable desde el instalador de Vivado 2023.2.
-
LED no parpadea (siempre apagado):
- Asegúrate de que el nombre del puerto en Verilog es exactamente “led” y en XDC usas [get_ports { led }].
- Revisa la asignación de pin del LED (U16) y que el IOSTANDARD sea LVCMOS33.
-
Comprueba que no has definido un reset que deje el led en 0 perpetuamente (en este diseño no hay reset).
-
LED no parpadea (siempre encendido):
- Verifica que el contador está incrementando: HALF_PERIOD_TICKS = 49_999_999 es correcto para 0.5 s a 100 MHz.
-
Si alteraste el código, revisa que led <= ~led en el punto de rollover del contador.
-
Violaciones de temporización tras implementación:
- Para este diseño no debería ocurrir. Si ocurre, limpia la carpeta vivado/ y reconstruye.
-
Asegúrate de usar “create_clock -period 10.000” en XDC.
-
Error de versión/part inconsistente:
- Comprueba que el part en el build.tcl sea exactamente xc7a35tcpg236-1.
-
El top del diseño debe llamarse “top” y coincidir con set_property top top.
-
El bitstream no se genera:
- Revisa la consola de Vivado por errores de sintaxis en Verilog o XDC.
-
Asegúrate de que las rutas src/ y constraints/ contienen los ficheros con los nombres exactos.
-
Parpadeo con frecuencia incorrecta (demasiado rápido o lento):
- Verifica que la Basys 3 está usando el reloj de 100 MHz (pin W5) y no un reloj de usuario distinto.
- Revisa HALF_PERIOD_TICKS y el período de create_clock (10 ns = 100 MHz).
-
Si tu placa fuera no-Basys3 o con reloj distinto, ajusta los valores; aquí debe ser Basys 3 exactamente.
-
Programación falla intermitente:
- Desconecta y reconecta el USB.
- Cierra otras herramientas que usen JTAG (otra instancia de Vivado).
- Ejecuta program.tcl nuevamente.
Mejoras y variantes
- Frecuencia de parpadeo parametrizable:
- Añade parámetros para FCLK_HZ y BLINK_HZ y calcula HALF_PERIOD_TICKS automáticamente.
-
Ejemplo (solo idea): HALF_PERIOD_TICKS = (FCLK_HZ/(2*BLINK_HZ)) – 1.
-
Múltiples LEDs secuenciales:
- Extiende el contador y genera un registro de desplazamiento que haga correr un “1” por LED[15:0].
-
Validación visual más rica para practicar mapeos XDC adicionales.
-
Interruptor para cambiar velocidad:
- Lee SW0–SW3 para seleccionar diferentes divisores (p. ej., 1 Hz, 2 Hz, 4 Hz, 8 Hz).
-
Incluye anti-rebote para pulsadores si decides integrarlos.
-
Uso de MMCM/PLL:
- Aunque no es necesario, puedes generar un reloj más bajo (p. ej., 1 MHz) con un MMCM y luego un contador más corto.
-
Requiere el IP Catalog de Vivado (Clocking Wizard), manteniendo la toolchain 2023.2.
-
Simulación:
- Crea un testbench simple que acelere el tiempo de conteo (reducir HALF_PERIOD_TICKS a 10–100 para simular).
-
Usa xsim en Vivado (WebPACK) para validar la lógica antes de programar.
-
Reportes y buenas prácticas:
- Analiza report_utilization para conocer LUT/FF usados (deberían ser mínimos).
- Añade atributos de síntesis si luego integras en un diseño mayor (p. ej., KEEP).
Checklist de verificación
Usa esta lista para confirmar que has completado todas las etapas con éxito:
- [ ] Vivado 2023.2 (WebPACK) instalado y “vivado -version” funciona.
- [ ] Estructura de proyecto creada: src/, constraints/, scripts/, vivado/.
- [ ] Archivo Verilog top.v ubicado en src/top.v con los puertos: input CLK100MHZ, output led.
- [ ] Archivo XDC en constraints/basys3_blink.xdc con:
- [ ] CLK100MHZ en pin W5, LVCMOS33, create_clock 10.000 ns.
- [ ] led en pin U16, LVCMOS33.
- [ ] Script Tcl build.tcl listo en scripts/ y ejecutado sin errores.
- [ ] Bitstream generado: vivado/basys3_blink/basys3_blink.runs/impl_1/top.bit.
- [ ] Script Tcl program.tcl ejecutado sin errores y dispositivo detectado en JTAG.
- [ ] LED LD0 parpadea a ~1 Hz (0.5 s ON, 0.5 s OFF).
- [ ] Reportes de temporización sin violaciones (timing_summary_impl_1.rpt).
- [ ] Variantes/mejoras anotadas para prácticas posteriores (opcional).
Apéndice: ejemplo de variantes de código (opcional)
Si deseas una versión parametrizable para reutilizar en otros proyectos:
// Variante parametrizable (no necesaria para el caso básico)
module top_param #(
parameter integer FCLK_HZ = 100_000_000,
parameter integer BLINK_HZ = 1
) (
input wire CLK100MHZ,
output reg led
);
localparam integer HALF_PERIOD_TICKS = (FCLK_HZ/(2*BLINK_HZ)) - 1;
localparam integer CNT_WIDTH = $clog2(HALF_PERIOD_TICKS+1);
reg [CNT_WIDTH-1:0] cnt = {CNT_WIDTH{1'b0}};
always @(posedge CLK100MHZ) begin
if (cnt == HALF_PERIOD_TICKS[CNT_WIDTH-1:0]) begin
cnt <= {CNT_WIDTH{1'b0}};
led <= ~led;
end else begin
cnt <= cnt + 1'b1;
end
end
endmodule
Nota: En Vivado 2023.2, $clog2 es soportado. Aun así, para el caso básico la versión fija es suficiente y más transparente para un primer contacto.
Cierre
Has completado un flujo de trabajo completo con Basys 3 (Xilinx Artix‑7) usando Vivado 2023.2 WebPACK: desde la asignación de pines y definición de reloj, hasta la síntesis, implementación, bitstream y programación por JTAG, logrando un “led-blink-with-clock-divider” robusto y reproducible. Este ejemplo sienta las bases para diseños más complejos, manteniendo disciplina en la estructura de proyecto, versionado de toolchain y automatización con Tcl.
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.



