Tarea 4: El Oscilador Caótico Funcionando en el FPGA

Tarea 4: El Oscilador Caótico Funcionando en el FPGA

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:


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

  1. Paso 1 — Verificación en C: Validar la aritmética de punto fijo antes de la síntesis
  2. Paso 2 — Síntesis en FPGA: Compilar el diseño Verilog y verificar recursos
  3. Paso 3 — Extracción serial: Obtener datos X, Y, Z vía UART
  4. 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

Simulador del Sistema Lü — lu_q14_18.c

#include <stdio.h>
#include "intarith.h"
// Sistema Lü - Función no lineal por segmentos
long 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

Terminal window
gcc -O2 -o lu_q14_18 lu_q14_18.c intarith.c -lm
./lu_q14_18

Resultado Esperado


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

ArchivoDescripción
main.vMódulo top-level (reset, LEDs, UART TX, FSM de envío)
lu.vNúcleo computacional del sistema caótico
oscilador_lu.vWrapper de control (registros, MUXes, contador)
pins.pcfAsignación de pines de la Alchitry Cu
Modules/uart_tx.vTransmisor 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
endmodule

Nú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));
endmodule

Wrapper 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)
);
endmodule

Mó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
endmodule

Asignación de Pines — pins.pcf

# Alchitry Cu - iCE40-HX8K-CB132
# Reloj del sistema: 100 MHz
set_io clk P7
# 8 LEDs integrados (active-high)
set_io led[0] J11
set_io led[1] K11
set_io led[2] K12
set_io led[3] K14
set_io led[4] L12
set_io led[5] L14
set_io led[6] M12
set_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 M9

Uso de Recursos

RecursoUsadosDisponiblesUso (%)
ICESTORM_LC (LUTs)3728768048.5%
ICESTORM_RAM0320.0%
SB_IO112564.3%
SB_GB (Global Buf)5862.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

Terminal window
apio build # Yosys + Nextpnr + Icepack
apio upload # Resultado esperado: cdone: high

Verificació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ámetroValor
Puerto/dev/ttyUSB1 (FTDI Canal B)
Baud rate115200
Formato8N1 (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, 60
SYNC = 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,Z

Ejecución

Terminal window
pip install pyserial
python3 capturar_serial.py

Si hay error de permisos:

Terminal window
sudo chmod 666 /dev/ttyUSB1

Formato de Salida (datos_serial.txt)

muestra,X,Y,Z
1,4.495705,-4.169044,-4.366085
2,4.282715,-4.383011,-4.221897
3,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

Terminal window
# 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_c

Gráficas Generadas

  1. Diagramas de fase 2D — X-Y, X-Z, Y-Z (3 espirales)
  2. Atractor 3D — Vista tridimensional del atractor caótico
  3. Series de tiempo — X(t), Y(t), Z(t) vs muestra

Diagramas de Fase (Datos FPGA)

Diagramas de fase del oscilador — FPGA

Atractor 3D (Datos FPGA)

Atractor 3D — FPGA

Series de Tiempo (Datos FPGA)

Series de tiempo — 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:

Diagramas de fase — Verificación C

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.