FPGA & Digital Systems Design Portfolio

Simón Aulet · Verilog · Artix-7 · Sistemas Digitales

Este repositorio compila una serie de proyectos de diseño lógico implementados en Verilog para la placa de desarrollo Artix-7. Los diseños abarcan desde lógica combinacional fundamental hasta máquinas de estados finitos (FSM) complejas e interactivas.

FPGA Verilog Artix-7 Sistemas Digitales FSM

Sistema de Alarma de 3 Estados

Este proyecto implementa un sistema de control de seguridad residencial basado en una arquitectura de hardware síncrona. El núcleo del diseño es una máquina de estados finitos que administra tres modos de operación: Desarmado, Armado y Alarma Activa.

Código Fuente: https://github.com/SimonAulet/portfolio/tree/main/FPGA/Alarm

Arquitectura del Sistema de Alarma

Arquitectura del sistema: flujo de señales entre la detección de secuencia y el control de estados.

Lógica de Control y Funcionamiento

La interfaz de usuario consta de tres pulsadores de combinación (b1, b2, b3) y un comando de validación (check). El sistema evalúa la secuencia ingresada únicamente cuando se presiona la validación, lo que permite una lógica de control robusta: si el usuario intenta validar una secuencia incorrecta mientras el sistema está armado, la transición es inmediata hacia el estado de Alarma. Del mismo modo, el estado de alerta se dispara ante la activación del sensor de movimiento (MOV).

Estrategia de Sincronización

Para garantizar la estabilidad operativa dentro de la FPGA y evitar la creación de múltiples dominios de reloj, se optó por una estrategia de habilitadores síncronos. En lugar de dividir la línea de reloj principal —con sus correspondientes problemas de timing— se implementó el módulo freq_divider.

Este componente genera pulsos de un solo ciclo ("ticks") que habilitan procesos a frecuencias menores (como 1 kHz y 1 Hz), manteniendo todo el diseño perfectamente sincronizado al reloj maestro de 100 MHz. Además, el módulo es totalmente parametrizable, lo que permite ajustar las escalas de división para acelerar los tiempos en etapas de simulación o definir los valores finales para la síntesis.

Desplegar código: Generador de Ticks (freq_divider.v)
always @(posedge clk_in)
                  // Pulse generator at 1kHz and 1 Hz

                  module freq_divider #(
                    parameter mf_divider = 100_000,
                    parameter lf_divider = 1_000)
                  (
                  input  wire clk_in,
                  output reg  tick_mf,
                  output reg  tick_lf
                  );

                  reg [18:0] mf_counter;
                  reg [8:0] lf_counter;

                  initial
                  begin
                    tick_mf    = 0;
                    tick_lf    = 0;
                    mf_counter = 0;
                    lf_counter = 0;
                  end
                  // Counter advancing
                  always @(posedge clk_in)
                  begin
                    if (mf_counter == mf_divider - 1) //mf counter. division is for switching posedge and negedge
                    begin
                      mf_counter    <= 0;
                      if(lf_counter == lf_divider - 1) //lf counter
                        lf_counter  <= 0;
                      else
                        lf_counter <= lf_counter + 1;
                    end else begin
                      mf_counter   <= mf_counter + 1;
                    end
                  end

                  // Freq divider lf
                  always @(posedge clk_in)
                  begin
                    if((lf_counter == 0) && (mf_counter==0))
                      tick_lf <= 1'b1;
                    else
                      tick_lf <= 1'b0;
                  end
                  // Freq divider mf
                  always@(posedge clk_in)
                  begin
                    if(mf_counter == 0)
                      tick_mf <= 1'b1;
                    else
                      tick_mf <= 1'b0;
                  end
                  endmodule
                end
                

Optimización de Recursos

El acondicionamiento de las señales de entrada se beneficia directamente de la estrategia de sincronización anterior. El módulo anti_bounce utiliza los ticks de 1 kHz como referencia temporal para filtrar el ruido mecánico de los pulsadores. Esta decisión de diseño permite reducir significativamente el uso de registros: la ventana de estabilidad de 20 ms se gestiona con un contador compacto de 6 bits, evitando la necesidad de contadores de 21 bits que serían requeridos si se operara directamente sobre la frecuencia base de 100 MHz.

Verificación

La confiabilidad del sistema se aseguró mediante una metodología de validación incremental. Los módulos fueron sometidos a testbenches dedicados para verificar correcto funcionamiento antes de su integración final en la entidad superior.

Secuenciador de Luces (Máquina de Moore)

Este diseño implementa un secuenciador de efectos lumínicos controlado por un único pulsador. La arquitectura se basa estrictamente en el modelo de Máquina de Moore, donde las salidas dependen exclusivamente del estado actual y no de las entradas directas.

Código Fuente: https://github.com/SimonAulet/portfolio/tree/main/FPGA/Moore_seq

Arquitectura Desacoplada

Para maximizar la modularidad, se dividió el sistema en dos bloques funcionales independientes que operan bajo el mismo dominio de reloj (100 MHz), sincronizados mediante las señales de habilitación (ticks) heredadas del diseño anterior:

  1. Control de Estados (state_change.v): Gestiona la lectura del pulsador, el debouncing (usando tick_mf) y las transiciones de estados.
  2. Decodificación de Salida (led_change.v): Interpreta el estado actual y genera los patrones visuales correspondientes.

Lógica de Transición y Salida

El sistema cicla a través de 4 estados operativos con cada pulsación validada. La lógica de salida aprovecha la señal de baja frecuencia (tick_lf de 1 Hz) para generar efectos de parpadeo sin necesidad de contadores adicionales dentro del módulo de LEDs.

  • Estado 00 (IDLE): Sistema en reposo, salidas apagadas.
  • Estado 01: LED A parpadeando a 1 Hz.
  • Estado 10: LED B parpadeando a 1 Hz.
  • Estado 11: Ambos LEDs parpadeando sincronizados.

Esta separación permite alterar los patrones lumínicos (ej: cambiar la frecuencia o el patrón de bit) modificando únicamente el módulo de salida, sin riesgo de alterar la lógica de control de flujo.

Diagrama de Estados Secuenciador

Diagrama de estados del secuenciador (Moore)

Desplegar código: Lógica de Salida (led_change.v)
module led_change(
                  input  wire[1:0] state,
                  input  wire      clk,
                  input  wire      tick_lf,
                  output reg       led_a,
                  output reg       led_b
                );

                initial
                begin
                  led_a = 1'b0;
                  led_b = 1'b0;
                end

                always@(posedge clk)
                  case(state)
                  2'b00:
                  begin
                    led_a <= 0;
                    led_b <= 0;
                  end
                  2'b01:
                  begin
                    if(tick_lf)
                      led_a <= ~led_a;
                    else
                      led_a <= led_a;
                    led_b <= 0;
                  end
                  2'b10:
                  begin
                    led_a <= 0;
                    if(tick_lf)
                      led_b <= ~led_b;
                    else
                      led_b <= led_b;
                  end
                  2'b11:
                  begin
                    if(tick_lf)
                      led_a <= ~led_a;
                    else
                      led_a <= led_a;
                      led_b <= led_a;  //copy led_a to avoid mirror blink
                  end
                  default:
                  begin
                    led_a <= 0;
                    led_b <= 0;
                  end
                endcase
                endmodule
                

Fundamentos de Verilog

Esta sección explora la construcción de hardware digital desde sus bloques más elementales, enfocándose en la modularidad, parametrización y máquinas de estados.

Código Fuente: https://github.com/SimonAulet/portfolio/tree/main/FPGA/Verilog

Diseño Jerárquico: Sumador Completo (Ej. 10 y 11)

Este módulo ilustra la metodología de diseño "bottom-up". En lugar de describir la lógica de suma completa de una vez, se construyó encapsulando componentes de menor nivel:

  • Nivel 1 (Half Adder): Resuelve la suma de 1 bit sin acarreo de entrada.
  • Nivel 2 (Full Adder): Instancia dos Half Adders y lógica de glue (OR) para gestionar el Acarreo (Carry).

Esta estructura permite escalar fácilmente hacia un Sumador de 4-bits (Ripple Carry) reutilizando el módulo validado, base fundamental para la ALU.

Esquemático Full Adder

Arquitectura con 2 Half-Adders


Simulación Full Adder

Validación de tabla de verdad

Unidad Aritmético Lógica (ALU) de 4-Bits (Ej. 12)

Integración de lógica combinacional aritmética y lógica en un solo núcleo. La ALU realiza 4 operaciones seleccionables mediante un Opcode de 2 bits:

  • Suma (ADD): Utiliza el módulo nibble_adder derivado del ejercicio anterior.
  • Resta (SUB): Implementa complemento a 2 invirtiendo el operando B y forzando el Carry-In.
  • Lógica (AND / OR): Operaciones bit a bit.

En las simulaciones se valida tanto el manejo de signo (formato decimal) como la operación bit a bit (formato binario).

Simulación ALU Decimal

Aritmética: Suma y Resta (Signo)


Simulación ALU Binaria

Lógica: AND y OR (Bitwise)

Contador Parametrizable "Módulo-N" (Ej. 18)

Un diseño de lógica secuencial flexible que permite definir el límite de cuenta (N) dinámicamente mediante una entrada de 8 bits. Se validaron 3 escenarios críticos:

  • Inicio de Cuenta: Verificación del conteo básico ascendente desde 0 hasta N.
  • Cambio Dinámico de N: Se modificó el valor de N durante la ejecución. El sistema adapta su comparador instantáneamente sin requerir un reset, continuando la cuenta hasta el nuevo límite.
  • Reset Asíncrono: Verificación de la prioridad de la señal de reset, que fuerza la salida a 0 independientemente del reloj.
Inicio del contador

1. Inicio de cuenta (0 a 5)


Cambio de modulo

2. Cambio de N "on-the-fly"


Reset del contador

3. Reset Asíncrono

Detector de Secuencia "101" (Ej. 20)

Implementación de una Máquina de Estados de Moore que analiza una entrada serial bit a bit.

Diseñado para permitir solapamiento (overlap). Por ejemplo, en la secuencia 10101, el sistema activa la detección dos veces (el "1" final de la primera detección sirve como el "1" inicial de la siguiente), demostrando una lógica de control de flujo continua.

FSM Detector 101