Información de la Tarea
Estudiante: Andrés Cruz Chipol
Curso: Arquitectura De Computadoras
Fecha de entrega: Lunes 17 de Febrero, 2026
Descripción de la Tarea
Implementar el oscilador caótico de Lü en el FPGA y cumplir los siguientes requisitos:
- Extraer los datos del FPGA usando la comunicación serial
- Verificar que el diseño quepa en los recursos disponibles en el FPGA; si no es así, reducir el número de bits de la representación
- Verificar primero en software en C que el oscilador funciona en la representación escogida
- Visualizar en la computadora los diagramas de fase entre las variables del oscilador
Implementación del Oscilador Caótico de Lü en FPGA
Introducción
La Tarea 4 extiende el diseño Verilog desarrollado en la Tarea 3 ejecutándolo en hardware real: la FPGA Alchitry Cu (iCE40-HX8K-CB132). El flujo de trabajo se organizó en cuatro pasos secuenciales para garantizar la correcta validación antes de la implementación física.
Arquitectura del Flujo de Trabajo
- Paso 1 — Verificación en C: Validar la aritmética de punto fijo antes de la síntesis
- Paso 2 — Síntesis en FPGA: Compilar el diseño Verilog y verificar recursos
- Paso 3 — Extracción serial: Obtener datos X, Y, Z vía UART
- Paso 4 — Visualización: Generar diagramas de fase y atractor 3D
Paso 1: Verificación en Software (C)
Objetivo
Verificar que el oscilador caótico de Lü funciona correctamente con la aritmética de punto fijo Q14.18 (14 bits enteros, 18 bits fraccionarios) antes de implementarlo en hardware.
Archivos
lu_q14_18.c— Simulación del sistema Lü en C (usaintarith.heintarith.cpara aritmética de punto fijo)datos.txt— Datos de salida (t, x, y, z) en decimal
Simulador del Sistema Lü — lu_q14_18.c
#include <stdio.h>#include "intarith.h"
// Sistema Lü - Función no lineal por segmentoslong f_pwl(long u) { long lim_a = setNumber(0.9), lim_b = setNumber(1.1); long slope = setNumber(10.0), sat = setNumber(2.0);
if (u > lim_b) return sat; if (u < -lim_b) return -sat; if (u > lim_a) return mulTrunc(slope, u - lim_a); if (u < -lim_a) return mulTrunc(slope, u + lim_a); return 0;}
int main() { // Inicializar aritmética de punto fijo: 14 bits enteros, 18 bits fraccionarios initializeA(14, 18); FILE *fp = fopen("datos.txt", "w");
// Parámetros del sistema long a = setNumber(0.7); long k = setNumber(16.0); long inv_k = setNumber(1.0/16.0); double h_val = 0.0001; long h = setNumber(h_val);
// Configuración de tiempos double t_total = 100.0; long pasos_totales = (long)(t_total / h_val); // 1,000,000 pasos int pasos_para_imprimir = 100; // Imprimir cada 0.01s
// Estado inicial long x[3] = {setNumber(5.0), setNumber(5.0), 0}; long d[3];
// Variables para análisis de amplitud long max_x = setNumber(-1000), min_x = setNumber(1000); long max_y = setNumber(-1000), min_y = setNumber(1000); long max_z = setNumber(-1000), min_z = setNumber(1000);
printf("Calculando simulacion precisa (h=0.0001) para X, Y, Z...\n");
for(long i = 0; i < pasos_totales; i++) { // Guardar dato cada 0.01s en archivo if(i % pasos_para_imprimir == 0) { fprintf(fp, "%f %f %f %f\n", i*h_val, getNumber(x[0]), getNumber(x[1]), getNumber(x[2])); }
// Análisis de amplitud (ignorar primeros 20s = 200,000 pasos) if(i > 200000) { if(x[0] > max_x) max_x = x[0]; if(x[0] < min_x) min_x = x[0]; if(x[1] > max_y) max_y = x[1]; if(x[1] < min_y) min_y = x[1]; if(x[2] > max_z) max_z = x[2]; if(x[2] < min_z) min_z = x[2]; }
// Método de Euler d[0] = x[1]; d[1] = x[2];
long arg = mulTrunc(x[0], inv_k); long f_val = f_pwl(arg); long nonlinear = mulTrunc(a, mulTrunc(k, f_val));
d[2] = -mulTrunc(a, x[0]) - mulTrunc(a, x[1]) - mulTrunc(a, x[2]) + nonlinear;
x[0] += mulTrunc(h, d[0]); x[1] += mulTrunc(h, d[1]); x[2] += mulTrunc(h, d[2]); }
fclose(fp);
// Imprimir resultados printf("\n=== RESULTADOS C (Punto Fijo) ===\n"); printf("[C-Fixed] X: Amp=%.4f [Min=%.4f, Max=%.4f]\n", getNumber(max_x - min_x), getNumber(min_x), getNumber(max_x)); printf("[C-Fixed] Y: Amp=%.4f [Min=%.4f, Max=%.4f]\n", getNumber(max_y - min_y), getNumber(min_y), getNumber(max_y)); printf("[C-Fixed] Z: Amp=%.4f [Min=%.4f, Max=%.4f]\n", getNumber(max_z - min_z), getNumber(min_z), getNumber(max_z));
return 0;}Compilación y Ejecución
gcc -O2 -o lu_q14_18 lu_q14_18.c intarith.c -lm./lu_q14_18Resultado Esperado
- X oscila entre -45.7 y +49.5 (3 espirales)
- Y oscila entre -18.2 y +14.7
- Z oscila entre -14.7 y +11.7
- Se generan
datos.txtydatos_hex.txt
Paso 2: Síntesis e Implementación en FPGA
Objetivo
Sintetizar el diseño Verilog y programar la FPGA Alchitry Cu (iCE40-HX8K-CB132), verificando que el diseño quepa en los recursos disponibles.
Síntesis y optimizaciones
Para la implementación en FPGA se utilizó corrimiento de bits únicamente en los módulos de punto fijo y en el oscilador: el truncamiento en las multiplicaciones se hace por corrimiento a la derecha (selección de bits) en lugar de división, y el oscilador parametriza el contador de control para ajustar la velocidad de iteración. Además se agregó el módulo uart_tx.v para la comunicación serial con el PC. El resto del diseño reutiliza los módulos de la Tarea 3 (adder, subtractor, multiplier, register, ffd, counter).
Archivos del Diseño
| Archivo | Descripción |
|---|---|
main.v | Módulo top-level (reset, LEDs, UART TX, FSM de envío) |
lu.v | Núcleo computacional del sistema caótico |
oscilador_lu.v | Wrapper de control (registros, MUXes, contador) |
pins.pcf | Asignación de pines de la Alchitry Cu |
Modules/uart_tx.v | Transmisor UART 8N1 |
Módulo Top-Level — main.v
/** * Modulo Top-Level: main.v * Plataforma: Alchitry Cu (iCE40-HX8K-CB132) * * Funcionalidad: * 1. Ejecuta el oscilador caotico de Lu a maxima velocidad. * 2. Los 8 LEDs muestran la parte entera de X (~6 Hz). * 3. Envia X, Y, Z por UART (115200 baud) al PC cada ~10ms * para graficar en tiempo real. * * Protocolo UART (binario, 14 bytes por muestra): * [0xAA] [0x55] [X3 X2 X1 X0] [Y3 Y2 Y1 Y0] [Z3 Z2 Z1 Z0] * Sync header X (MSB first) Y (MSB first) Z (MSB first) * -> ~100 muestras/segundo a 115200 baud **/module main ( input wire clk, // 100 MHz (P7) input wire rst_n, // Reset activo-bajo (P8) output reg [7:0] led, // 8 LEDs output wire uart_tx // UART TX -> PC (M9));
reg [3:0] por_cnt = 4'd0; wire por_active = (por_cnt != 4'hF);
always @(posedge clk) if (por_active) por_cnt <= por_cnt + 1'b1;
reg [2:0] rst_sync; wire rst;
always @(posedge clk) rst_sync <= {rst_sync[1:0], ~rst_n};
assign rst = por_active | rst_sync[2];
localparam N = 32; localparam FRAC = 18;
// Condiciones iniciales: x0=5.0, y0=5.0, z0=0.0 localparam signed [N-1:0] X0 = 32'sd1310720; // 5.0 en Q14.18 (5*262144) localparam signed [N-1:0] Y0 = 32'sd1310720; // 5.0 en Q14.18 localparam signed [N-1:0] Z0 = 32'sd0; // 0.0
reg [1:0] startup_cnt; reg stf;
always @(posedge clk or posedge rst) begin if (rst) begin startup_cnt <= 2'd0; stf <= 1'b0; end else begin case (startup_cnt) 2'd0: begin stf <= 1'b1; startup_cnt <= 2'd1; end 2'd1: begin stf <= 1'b0; startup_cnt <= 2'd2; end default: stf <= 1'b0; endcase end end
wire signed [N-1:0] x_val, y_val, z_val; wire eof;
oscilador_lu #(.n(N), .frac(FRAC), .CNT_W(9), .CNT_MAX(300)) u_osc ( .CLK(clk), .RST(rst), .STF(stf), .X0(X0), .Y0(Y0), .Z0(Z0), .X(x_val), .Y(y_val), .Z(z_val), .EOF(eof) );
reg [23:0] led_prescaler; wire led_tick = (led_prescaler == 24'd0);
always @(posedge clk or posedge rst) begin if (rst) led_prescaler <= 24'd0; else led_prescaler <= led_prescaler + 1'b1; end
always @(posedge clk or posedge rst) begin if (rst) led <= 8'b10101010; else if (led_tick) led <= x_val[25:18]; end
reg [7:0] tx_data; reg tx_start; wire tx_busy;
uart_tx #( .CLK_FREQ(100_000_000), .BAUD(115_200) ) u_uart ( .clk(clk), .rst(rst), .data(tx_data), .start(tx_start), .tx(uart_tx), .busy(tx_busy) );
localparam SEND_INTERVAL = 20'd150_000; reg [19:0] send_timer; reg send_trigger;
always @(posedge clk or posedge rst) begin if (rst) begin send_timer <= 20'd0; send_trigger <= 1'b0; end else begin if (send_timer == SEND_INTERVAL - 1) begin send_timer <= 20'd0; send_trigger <= 1'b1; end else begin send_timer <= send_timer + 1'b1; send_trigger <= 1'b0; end end end
localparam TX_IDLE = 5'd0; localparam TX_SEND = 5'd1; localparam TX_WAIT = 5'd2;
reg [4:0] tx_state; reg [3:0] tx_byte_idx; reg [N-1:0] tx_x, tx_y, tx_z; reg [7:0] tx_buf [0:13];
always @(posedge clk or posedge rst) begin if (rst) begin tx_state <= TX_IDLE; tx_byte_idx <= 4'd0; tx_start <= 1'b0; tx_data <= 8'd0; end else begin case (tx_state) TX_IDLE: begin tx_start <= 1'b0; if (send_trigger) begin tx_x <= x_val; tx_y <= y_val; tx_z <= z_val; tx_buf[0] <= 8'hAA; tx_buf[1] <= 8'h55; tx_buf[2] <= x_val[31:24]; tx_buf[3] <= x_val[23:16]; tx_buf[4] <= x_val[15:8]; tx_buf[5] <= x_val[7:0]; tx_buf[6] <= y_val[31:24]; tx_buf[7] <= y_val[23:16]; tx_buf[8] <= y_val[15:8]; tx_buf[9] <= y_val[7:0]; tx_buf[10] <= z_val[31:24]; tx_buf[11] <= z_val[23:16]; tx_buf[12] <= z_val[15:8]; tx_buf[13] <= z_val[7:0]; tx_byte_idx <= 4'd0; tx_state <= TX_SEND; end end TX_SEND: begin if (!tx_busy) begin tx_data <= tx_buf[tx_byte_idx]; tx_start <= 1'b1; tx_state <= TX_WAIT; end end TX_WAIT: begin tx_start <= 1'b0; if (!tx_busy && !tx_start) begin if (tx_byte_idx == 4'd13) tx_state <= TX_IDLE; else begin tx_byte_idx <= tx_byte_idx + 1'b1; tx_state <= TX_SEND; end end end default: tx_state <= TX_IDLE; endcase end end
endmoduleNúcleo del Sistema Caótico — lu.v
Este módulo implementa las ecuaciones diferenciales del sistema de Lü con el método de Euler. Los parámetros del sistema se precalcularon como constantes enteras en formato Q14.18 directamente en los parámetros del módulo:
/** * Modulo: lu.v * Descripcion: Sistema Lu - Formato: Q14.18 (1.0 = 262144) **/module lu #( parameter n = 32, parameter frac = 18,
// PARAMETROS DEL SISTEMA (precalculados en Q14.18) parameter signed [31:0] PARAM_A = 32'd183500, // a = 0.7 parameter signed [31:0] PARAM_H = 32'd26, // h = 0.0001 parameter signed [31:0] VAL_K = 32'd4194304, // k = 16.0 parameter signed [31:0] VAL_INV_K = 32'd16384, // 1/k = 0.0625
// PARAMETROS PWL parameter signed [31:0] LIM_A = 32'd235929, // 0.9 parameter signed [31:0] LIM_B = 32'd288358, // 1.1 parameter signed [31:0] SLOPE = 32'd2621440, // 10.0 parameter signed [31:0] SAT = 32'd524288 // 2.0)( input CLK, RST, input signed [n-1:0] x_in, y_in, z_in, output signed [n-1:0] x_out, y_out, z_out);
wire signed [n-1:0] arg_pwl; multiplier #(.n(n),.frac(frac)) M_INVK (.A(x_in), .B(VAL_INV_K), .O(arg_pwl));
reg signed [n-1:0] f_val; wire signed [n-1:0] diff_pos, diff_neg, term_slope_pos, term_slope_neg; wire signed [31:0] LIM_B_NEG = -LIM_B; wire signed [31:0] LIM_A_NEG = -LIM_A; wire signed [31:0] SAT_NEG = -SAT;
subtractor #(.n(n)) SUB_P (.A(arg_pwl), .B(LIM_A), .O(diff_pos)); multiplier #(.n(n),.frac(frac)) M_SLOPE_P (.A(SLOPE), .B(diff_pos), .O(term_slope_pos)); adder #(.n(n)) ADD_N (.A(arg_pwl), .B(LIM_A), .O(diff_neg)); multiplier #(.n(n),.frac(frac)) M_SLOPE_N (.A(SLOPE), .B(diff_neg), .O(term_slope_neg));
always @(*) begin if (arg_pwl > LIM_B) f_val = SAT; else if (arg_pwl < LIM_B_NEG) f_val = SAT_NEG; else if (arg_pwl > LIM_A) f_val = term_slope_pos; else if (arg_pwl < LIM_A_NEG) f_val = term_slope_neg; else f_val = 32'd0; end
wire signed [n-1:0] k_fval, nonlinear; multiplier #(.n(n),.frac(frac)) M_K_F (.A(VAL_K), .B(f_val), .O(k_fval)); multiplier #(.n(n),.frac(frac)) M_NONLIN (.A(PARAM_A), .B(k_fval), .O(nonlinear));
wire signed [n-1:0] h_y; multiplier #(.n(n),.frac(frac)) M_HY (.A(PARAM_H), .B(y_in), .O(h_y)); adder #(.n(n)) ADD_X (.A(x_in), .B(h_y), .O(x_out));
wire signed [n-1:0] h_z; multiplier #(.n(n),.frac(frac)) M_HZ (.A(PARAM_H), .B(z_in), .O(h_z)); adder #(.n(n)) ADD_Y (.A(y_in), .B(h_z), .O(y_out));
wire signed [n-1:0] ax, ay, az, sum_xy, sum_lin, neg_sum_lin; multiplier #(.n(n),.frac(frac)) M_AX (.A(PARAM_A), .B(x_in), .O(ax)); multiplier #(.n(n),.frac(frac)) M_AY (.A(PARAM_A), .B(y_in), .O(ay)); multiplier #(.n(n),.frac(frac)) M_AZ (.A(PARAM_A), .B(z_in), .O(az));
adder #(.n(n)) A_XY (.A(ax), .B(ay), .O(sum_xy)); adder #(.n(n)) A_XYZ (.A(sum_xy), .B(az), .O(sum_lin)); subtractor #(.n(n)) S_NEG (.A(32'd0), .B(sum_lin), .O(neg_sum_lin));
wire signed [n-1:0] z_dot, h_zdot; adder #(.n(n)) A_ZDOT (.A(neg_sum_lin), .B(nonlinear), .O(z_dot)); multiplier #(.n(n),.frac(frac)) M_HZDOT (.A(PARAM_H), .B(z_dot), .O(h_zdot)); adder #(.n(n)) ADD_Z (.A(z_in), .B(h_zdot), .O(z_out));
endmoduleWrapper de Control — oscilador_lu.v
Controla la iteración del oscilador: multiplexores para seleccionar entre condiciones iniciales y valores iterados, registros de estado y un contador parametrizable:
module oscilador_lu #( parameter n = 32, parameter frac = 18, parameter CNT_W = 3, // Ancho del contador (bits) parameter CNT_MAX = 5 // Ciclos de reloj por iteracion)( input wire CLK, RST, STF, input wire signed [n-1:0] X0, Y0, Z0, output wire signed [n-1:0] X, Y, Z, output wire EOF);
wire signed [n-1:0] next_x, next_y, next_z; wire signed [n-1:0] srx, sry, srz; wire [CNT_W-1:0] count_debug; wire sel, OPR, start_node;
lu #(.n(n), .frac(frac)) u_core ( .CLK(CLK), .RST(RST), .x_in(X), .y_in(Y), .z_in(Z), .x_out(next_x), .y_out(next_y), .z_out(next_z) );
register #(.n(n)) reg_x (.RST(RST), .CLK(CLK), .ENB(OPR), .I(next_x), .O(srx)); register #(.n(n)) reg_y (.RST(RST), .CLK(CLK), .ENB(OPR), .I(next_y), .O(sry)); register #(.n(n)) reg_z (.RST(RST), .CLK(CLK), .ENB(OPR), .I(next_z), .O(srz));
assign X = sel ? srx : X0; assign Y = sel ? sry : Y0; assign Z = sel ? srz : Z0;
assign start_node = STF | sel;
ffd u_sel_ff ( .RST(RST), .CLK(CLK), .ENB(OPR), .D(1'b1), .Q(sel) );
counter #(.nc(CNT_W), .nclk1(CNT_MAX)) u_ctrl ( .RST(RST), .CLK(CLK), .STF(start_node), .OPR(OPR), .COUNT(count_debug), .EOF(EOF) );
endmoduleMódulo UART TX — uart_tx.v
Módulo agregado para la comunicación serial con el PC:
module uart_tx #( parameter CLK_FREQ = 100_000_000, parameter BAUD = 115_200)( input wire clk, input wire rst, input wire [7:0] data, input wire start, output reg tx, output wire busy);
localparam CLKS_PER_BIT = CLK_FREQ / BAUD;
localparam S_IDLE = 2'd0; localparam S_START = 2'd1; localparam S_DATA = 2'd2; localparam S_STOP = 2'd3;
reg [1:0] state; reg [15:0] clk_cnt; reg [2:0] bit_idx; reg [7:0] data_reg;
assign busy = (state != S_IDLE);
always @(posedge clk or posedge rst) begin if (rst) begin state <= S_IDLE; tx <= 1'b1; clk_cnt <= 16'd0; bit_idx <= 3'd0; data_reg <= 8'd0; end else begin case (state) S_IDLE: begin tx <= 1'b1; if (start) begin data_reg <= data; clk_cnt <= 16'd0; state <= S_START; end end S_START: begin tx <= 1'b0; if (clk_cnt == CLKS_PER_BIT - 1) begin clk_cnt <= 16'd0; bit_idx <= 3'd0; state <= S_DATA; end else clk_cnt <= clk_cnt + 1'b1; end S_DATA: begin tx <= data_reg[bit_idx]; if (clk_cnt == CLKS_PER_BIT - 1) begin clk_cnt <= 16'd0; if (bit_idx == 3'd7) state <= S_STOP; else bit_idx <= bit_idx + 1'b1; end else clk_cnt <= clk_cnt + 1'b1; end S_STOP: begin tx <= 1'b1; if (clk_cnt == CLKS_PER_BIT - 1) state <= S_IDLE; else clk_cnt <= clk_cnt + 1'b1; end endcase end end
endmoduleAsignación de Pines — pins.pcf
# Alchitry Cu - iCE40-HX8K-CB132
# Reloj del sistema: 100 MHzset_io clk P7
# 8 LEDs integrados (active-high)set_io led[0] J11set_io led[1] K11set_io led[2] K12set_io led[3] K14set_io led[4] L12set_io led[5] L14set_io led[6] M12set_io led[7] N14
# Boton de Reset (active-low, tiene pull-up interno)set_io rst_n P8
# UART (FTDI Canal B -> USB serial del PC)set_io uart_tx M9Uso de Recursos
| Recurso | Usados | Disponibles | Uso (%) |
|---|---|---|---|
| ICESTORM_LC (LUTs) | 3728 | 7680 | 48.5% |
| ICESTORM_RAM | 0 | 32 | 0.0% |
| SB_IO | 11 | 256 | 4.3% |
| SB_GB (Global Buf) | 5 | 8 | 62.5% |
Frecuencia máxima alcanzada: 24.19 MHz (restricción: 12 MHz)
Conclusión: El diseño cabe en el FPGA con margen suficiente (48.5% LUTs). No fue necesario reducir el número de bits de la representación Q14.18.
Compilación y Subida
apio build # Yosys + Nextpnr + Icepackapio upload # Resultado esperado: cdone: highVerificación Visual
Los 8 LEDs de la placa parpadean de forma irregular/caótica, confirmando que el oscilador está corriendo correctamente.
Paso 3: Extracción de Datos por Comunicación Serial
Objetivo
Leer los datos X, Y, Z del oscilador caótico desde el FPGA a través del puerto serial (UART) y guardarlos en archivo para su posterior visualización.
Configuración UART
| Parámetro | Valor |
|---|---|
| Puerto | /dev/ttyUSB1 (FTDI Canal B) |
| Baud rate | 115200 |
| Formato | 8N1 (8 bits, sin paridad, 1 bit de stop) |
| Protocolo | [0xAA][0x55][X:4bytes][Y:4bytes][Z:4bytes] = 14 bytes |
Script de Captura
#!/usr/bin/env python3"""Captura UART del FPGA."""
import sys, struct, time, serial
PORT, BAUD, DUR = '/dev/ttyUSB1', 115200, 60SYNC = bytes([0xAA, 0x55])f2f = lambda b: struct.unpack('>i', b)[0] / 262144.0 # Q14.18 -> float
ser = serial.Serial(PORT, BAUD, timeout=1)fp = open('datos_serial.txt', 'w')
# Decodifica cada paquete de 14 bytes, extrae X,Y,Z# y escribe en CSV: muestra,X,Y,ZEjecución
pip install pyserialpython3 capturar_serial.pySi hay error de permisos:
sudo chmod 666 /dev/ttyUSB1Formato de Salida (datos_serial.txt)
muestra,X,Y,Z1,4.495705,-4.169044,-4.3660852,4.282715,-4.383011,-4.2218973,4.059223,-4.589565,-4.068005...Video del Funcionamiento
A continuación se muestra el FPGA en funcionamiento con la captura serial en tiempo real y la gráfica del diagrama de fase X-Y:
Paso 4: Visualización de Diagramas de Fase
Objetivo
Graficar los datos extraídos del FPGA para visualizar los diagramas de fase entre las variables del oscilador caótico de Lü.
Uso del Script de Graficación
# Datos del FPGA (formato CSV: muestra,X,Y,Z)python3 graficar.py ../paso3_extraccion_serial/datos_serial.txt
# Datos de la verificación C (formato: t x y z)python3 graficar.py ../paso1_verificacion_c/datos.txt --formato-c --prefijo verificacion_cGráficas Generadas
- Diagramas de fase 2D — X-Y, X-Z, Y-Z (3 espirales)
- Atractor 3D — Vista tridimensional del atractor caótico
- Series de tiempo — X(t), Y(t), Z(t) vs muestra
Diagramas de Fase (Datos FPGA)
Atractor 3D (Datos FPGA)
Series de Tiempo (Datos FPGA)
Verificación C vs FPGA
Las gráficas generadas con los datos de la verificación en C (verificacion_c_*.png) permiten comparar el comportamiento esperado con los datos reales del FPGA:
Conclusiones
Se implementó exitosamente el oscilador caótico de Lü en la FPGA Alchitry Cu: se validó la aritmética Q14.18 en C antes de la síntesis, el diseño encajó en los recursos disponibles (48.5% de LUTs) sin reducir bits, se extrajeron datos por UART con un protocolo de 14 bytes y se visualizaron los diagramas de fase X-Y, X-Z y Y-Z junto con el atractor 3D. La coincidencia entre las trayectorias generadas en C y las obtenidas del FPGA valida la implementación del diseño Verilog en hardware real.