Guía de Estilo y Buenas Prácticas de Diseño VHDL

Asignatura: Electrónica Digital – 2º Grado en Ingeniería de Telecomunicaciones

1. Filosofía del Curso

VHDL es un lenguaje de descripción de hardware. No estamos programando software secuencial, estamos definiendo circuitos físicos que operan en paralelo.

  • Regla de Oro: Si no puedes dibujar el esquema del circuito (puertas y registros) en un papel, no escribas el código.

  • No utilices construcciones de VHDL que no se hayan explicado en clase.

    • Prohibido: variable, bucles while, alias, librerías no estándar, etc. Cíñete estrictamente a las estructuras enseñadas para garantizar que tu diseño sea correcto y sintetizable.

2. Formato y Legibilidad

El código se escribe una vez, pero se lee muchas veces (por ti, por tus compañeros y por el profesor). La claridad es necesaria.

2.1. Indentación (Sangrado)

  • Regla: Usa siempre 4 espacios.

  • Prohibido: No uses tabuladores (Tab). Configura tu editor (Vivado, VS Code) para insertar espacios al pulsar Tab.

  • Jerarquía: Todo bloque estructural (entity, architecture, process, if, case) debe estar indentado hacia la derecha respecto a su contenedor.

2.2. Comentarios

  • Cabecera: Todo fichero debe comenzar con un bloque indicando: Nombre, Autor y Descripción.

  • Comentar el «Por qué», no el «Qué»:

    • No comentes lo obvio (a <= b; -- Asigna b a a).

    • Comenta la intención funcional (count_next <= 0; -- Reinicia cuenta por fin de trama).

3. Convenciones de Nombres

3.1. Ficheros (.vhd)

  • Correspondencia: El nombre del fichero debe ser EXACTAMENTE igual al nombre de la entidad. - Ejemplo: entity contador → fichero contador.vhd.

  • Unicidad: Solo una entidad por fichero.

3.2. Testbenches

  • Se nombrarán igual que la entidad a probar con el sufijo _tb. - Ejemplo: contador_tb.vhd.

  • Se aplica la convención 3.1 para el nombre de la entidad del testbench.

3.3. Nombres de Arquitecturas

  • RTL: Para diseños sintetizables (lógica combinacional + registros).

  • Behavioral: Exclusivo para testbenches (modelos de simulación).

  • Structural: Solo para código que describe la interconexión de componentes.

3.4. Nombres de puertos

  • Siempre en MAYÚSCULAS (ej.: DATA_IN, BUSY, LED_OUT).

3.5. Señales reloj y reset

Utilizaremos los identificadores siguientes:

  • Reloj: CLK.

  • RESET: RST (RST_N si activo bajo).

3.6. Señales Internas

  • En minúsculas.

  • En diseño secuencial es obligatorio usar los sufijos:

    • _reg: salida del registro (valor actual).

    • _next: entrada del registro (valor futuro).

      signal senal_reg  : unsigned(3 downto 0); -- Salida del flip-flop (Q).
      signal senal_next : unsigned(3 downto 0); -- Entrada del flip-flop (D).
      

3.7. Señales activas a nivel bajo

  • Sufijo _n (interno) o _N (puerto) para señales activas a “0”.

3.8. Tipos y Enumerados

  • Tipos definidos: Sufijo _t (ej.: type estado_t is...).

  • Estados: En MAYÚSCULAS con prefijo identificativo (ej.: ST_IDLE, ST_RUN).

3.9. Constantes

  • En MAYÚSCULAS con prefijo C_ (ej.: C_MAX_COUNT).

3.9. Tabla Resumen

Ten esta tabla a mano mientras codificas.

Elemento

Formato

Sufijo/Prefijo

Ejemplo

Notas

Fichero VHDL

minúsculas

.vhd

contador.vhd

Idéntico a la entidad.

Testbench

minúsculas

_tb.vhd

contador_tb.vhd

Simulación.

Reloj

MAYÚSCULAS

N/A

CLK

Nombre estricto.

RST

MAYÚSCULAS

_N (activo bajo)

RST, RST_N

Nombre estricto.

Puertos (I/O)

MAYÚSCULAS

_N (activo bajo)

DATA_IN, BUSY

Señales internas

minúsculas

_n (activo bajo)

flag_enable

Nunca usar mayúsculas.

Reg. (Salida)

minúsculas

_reg

count_reg

Valor actual (Q).

Reg. (Entrada)

minúsculas

_next

count_next

Valor futuro (D).

Tipos de Datos

minúsculas

_t

type estado_t

Para FSMs.

Estados (Enum)

MAYÚSCULAS

ST_

ST_IDLE, ST_RX

Prefijo obligatorio.

Constantes

MAYÚSCULAS

C_

C_MAX_COUNT

Parámetros.

1. Tipos de Datos Permitidos

4.1. Librerías

  • Obligatorio: ieee.std_logic_1164.all y ieee.numeric_std.all.

  • Prohibido: Otras librerías (std_logic_arith, std_logic_unsigned, etc.).

4.2. Uso de Tipos

  • Puertos: Siempre std_logic o std_logic_vector.

  • Aritmética interna: Usar signed o unsigned y convertir a vector solo en la salida.

  • Máquinas de Estados: Usar tipos enumerados definidos por el usuario (type estado_t is...).

5. Reglas de Implementación

5.1. Lógica Combinacional Simple

Para multiplexores, puertas lógicas o decodificadores sencillos, NO utilices un proceso. Usa asignaciones concurrentes.

-- Correcto: Asignación concurrente
salida <= entrada1 when (a = '1' and b = '0') else entrada2;

5.2. Metodología de «Dos Procesos» (Diseño Secuencial)

Para contadores, registros de desplazamiento y FSMs, separa explícitamente el cálculo de la memoria.

  1. Proceso Combinacional (Cálculo de _next)

  • Calcula el siguiente estado basándose en entradas y estado actual (_reg).

  • Regla Anti-Latch: Asigna un valor por defecto a todas las señales que son salidas del proceso al inicio del mismo.

  1. Proceso Secuencial (Actualización de _reg)

    • Solo sensible a CLK y RST.

    • Su única tarea es _reg <= _next.

6. Ejemplo de Referencia

Fichero: control_temporizado.vhd

----------------------------------------------------------------------------------
-- Fichero: control_temporizado.vhd
-- Autor:   Estudiante de Teleco
-- Desc:    Ejemplo de FSM con Datapath siguiendo la Guía de Estilo v3.0
----------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity control_temporizado is
    port (
        CLK       : in    std_logic;
        RST       : in    std_logic;
        START     : in    std_logic;
        MAX_COUNT : in    std_logic_vector(3 downto 0);
        DONE      : out   std_logic
    );
end control_temporizado;

architecture RTL of control_temporizado is

    -- 1. Definición de Tipos (Sufijo _t y Prefijos ST_)

    type estado_t is (ST_IDLE, ST_COUNTING, ST_FINISH);

    -- 2. Señales Internas (minúsculas + _reg/_next)
    -- Control (FSM)
    signal state_reg   : estado_t;
    signal state_next  : estado_t;

    -- Datos (Aritmética usa unsigned)
    signal count_reg   : unsigned(3 downto 0);
    signal count_next  : unsigned(3 downto 0);
    signal count_init  : std_logic;
    signal count_inc   : std_logic;

begin

-- ------------------------------------------------------------- --
--        MEF codificada con metodología de "Dos Procesos"       --
-- ------------------------------------------------------------- --

    -- ------------------------------------------------------------
    -- Lógica Combinacional (Cálculo de _next)
    -- ------------------------------------------------------------
    comb_proc : process (state_reg, count_reg, START, MAX_COUNT)
    begin

        -- A. Valores por defecto (Anti-Latch)
        count_init <= '0';
        count_inc  <= '0';
        DONE       <= '0';

        state_next <= state_reg;

        -- B. Máquina de Estados
        case state_reg is

            when ST_IDLE =>
                count_init <= '1';   -- RST síncrono del contador
                if (START = '1') then
                    state_next <= ST_COUNTING;
                end if;

            when ST_COUNTING =>
                if (count_reg = unsigned(MAX_COUNT)) then
                    state_next <= ST_FINISH;
                else
                    count_inc <= '1'; -- Incremento del contador
                end if;

            when ST_FINISH =>
                DONE       <= '1';
                state_next <= ST_IDLE;

            when others =>
                state_next <= ST_IDLE;

        end case;

    end process comb_proc;

    -- ------------------------------------------------------------
    -- Proceso: Lógica Secuencial (Memoria)
    -- ------------------------------------------------------------
    sec_proc : process (CLK)
    begin

        if rising_edge(CLK) then
            if (RST = '1') then
                state_reg <= ST_IDLE;
            else
                state_reg <= state_next;
            end if;
        end if;

    end process sec_proc;

-- ------------------------------------------------------------- --
--      Contador separando parte combinacional y secuencial      --
-- ------------------------------------------------------------- --

    -- ------------------------------------------------------------
    -- Lógica Combinacional (Cálculo de _next)
    -- ------------------------------------------------------------

    count_next <= (others => '0') when (count_init = '1') else
                  (count_reg + 1) when (count_inc = '1') else
                   count_reg;

    -- ------------------------------------------------------------
    -- Proceso: Lógica Secuencial (Memoria)
    -- ------------------------------------------------------------
    sec_proc : process (CLK)
    begin

        if rising_edge(CLK) then
            if (RST = '1') then
                count_reg <= (others => '0');
            else
                count_reg <= count_next;
            end if;
        end if;

    end process sec_proc;

end RTL;