Aprovechando procedimientos en-proceso para diseños FSM más limpios en VHDL
En VHDL, un procedimiento puede conducir señales externas siempre que la señal permanezca en su ámbito. Mientras que un procedimiento declarado en una arquitectura no tiene acceso a señales externas —porque esas señales no son visibles en tiempo de compilación— un procedimiento definido dentro de un proceso puede leer y escribir libremente cualquier señal que el proceso pueda ver.
Usar procedimientos en-proceso es una forma efectiva de desenredar lógica repetitiva en máquinas de estados o protocolos complejos. Al encapsular acciones comunes —como una transición de estado temporizada— dentro de un procedimiento, reduce código boilerplate, mejora la legibilidad y hace que la intención de cada paso sea cristalina.
Por ejemplo, en un controlador de semáforo, renombrar operaciones a ChangeState(ToState, Minutes, Seconds) dice al lector exactamente qué está haciendo el código sin sumergirse en las asignaciones de variables subyacentes.
Ejercicio práctico
En un tutorial anterior, usamos una función impura para conducir una señal Counter y determinar cambios de estado. Sin embargo, no pudimos mover la asignación de State a esa función porque VHDL requiere un valor de retorno para cada llamada a función. En su lugar, un procedimiento —desprovisto de valor de retorno— encaja perfectamente para este caso de uso.
A continuación se muestra el testbench y módulo completos que demuestran una FSM de semáforo conducida por un procedimiento en-proceso.
Testbench
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T23_ProcedureInProcessTb is
end entity;
architecture sim of T23_ProcedureInProcessTb is
constant ClockFrequencyHz : integer := 100; -- 100 Hz
constant ClockPeriod : time := 1000 ms / ClockFrequencyHz;
signal Clk : std_logic := '1';
signal nRst : std_logic := '0';
signal NorthRed : std_logic;
signal NorthYellow : std_logic;
signal NorthGreen : std_logic;
signal WestRed : std_logic;
signal WestYellow : std_logic;
signal WestGreen : std_logic;
begin
i_TrafficLights : entity work.T23_TrafficLights(rtl)
generic map(ClockFrequencyHz => ClockFrequencyHz)
port map (
Clk => Clk,
nRst => nRst,
NorthRed => NorthRed,
NorthYellow=> NorthYellow,
NorthGreen => NorthGreen,
WestRed => WestRed,
WestYellow => WestYellow,
WestGreen => WestGreen
);
Clk <= not Clk after ClockPeriod / 2;
process is
begin
wait until rising_edge(Clk);
wait until rising_edge(Clk);
nRst <= '1';
wait;
end process;
end architecture;
Módulo de semáforo
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T23_TrafficLights is
generic(ClockFrequencyHz : integer);
port(
Clk : in std_logic;
nRst : in std_logic;
NorthRed : out std_logic;
NorthYellow: out std_logic;
NorthGreen : out std_logic;
WestRed : out std_logic;
WestYellow : out std_logic;
WestGreen : out std_logic
);
end entity;
architecture rtl of T23_TrafficLights is
type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest);
signal State : t_State;
signal Counter : integer range 0 to ClockFrequencyHz * 60;
begin
process(Clk) is
procedure ChangeState(
ToState : t_State;
Minutes : integer := 0;
Seconds : integer := 0
) is
variable TotalSeconds : integer;
variable ClockCycles : integer;
begin
TotalSeconds := Seconds + Minutes * 60;
ClockCycles := TotalSeconds * ClockFrequencyHz - 1;
if Counter = ClockCycles then
Counter <= 0;
State <= ToState;
end if;
end procedure;
begin
if rising_edge(Clk) then
if nRst = '0' then
State <= NorthNext;
Counter <= 0;
NorthRed <= '1';
NorthYellow<= '0';
NorthGreen <= '0';
WestRed <= '1';
WestYellow <= '0';
WestGreen <= '0';
else
NorthRed <= '0';
NorthYellow<= '0';
NorthGreen <= '0';
WestRed <= '0';
WestYellow <= '0';
WestGreen <= '0';
Counter <= Counter + 1;
case State is
when NorthNext =>
NorthRed <= '1';
WestRed <= '1';
ChangeState(StartNorth, Seconds => 5);
when StartNorth =>
NorthRed <= '1';
NorthYellow<= '1';
WestRed <= '1';
ChangeState(North, Seconds => 5);
when North =>
NorthGreen <= '1';
WestRed <= '1';
ChangeState(StopNorth, Minutes => 1);
when StopNorth =>
NorthYellow<= '1';
WestRed <= '1';
ChangeState(WestNext, Seconds => 5);
when WestNext =>
NorthRed <= '1';
WestRed <= '1';
ChangeState(StartWest, Seconds => 5);
when StartWest =>
NorthRed <= '1';
WestRed <= '1';
WestYellow <= '1';
ChangeState(West, Seconds => 5);
when West =>
NorthRed <= '1';
WestGreen <= '1';
ChangeState(StopWest, Minutes => 1);
when StopWest =>
NorthRed <= '1';
WestYellow <= '1';
ChangeState(NorthNext, Seconds => 5);
end case;
end if;
end if;
end process;
end architecture;
Forma de onda
La simulación permanece idéntica a la FSM original —solo el código se ha vuelto más mantenible y fácil de entender.
Análisis
Al mover la lógica de temporización y cambio de estado a un solo procedimiento, preservamos el comportamiento del módulo mientras mejoramos dramáticamente la legibilidad. El procedimiento asegura una implementación consistente donde sea que se llame.
Conclusiones clave
- Un procedimiento dentro de un proceso puede acceder a cada señal visible para ese proceso.
- Encapsular lógica repetitiva en procedimientos mejora la claridad y mantenibilidad del código.
VHDL
- Dominando las Técnicas de Afiladora de Herramientas: Guía Práctica
- Cómo el molibdeno moldea la industria moderna: usos, beneficios y dinámica de suministro
- Introducción a VHDL: Construyendo tu primera compuerta AND
- Ejemplo de procedimiento VHDL: Incrementando un vector Standard Logic
- Creación de listas de cadenas en VHDL: Buenas prácticas y ejemplo
- Uso de funciones impuras en VHDL: Mejorando legibilidad y mantenibilidad de FSM
- Dominando funciones VHDL: Guía práctica para diseño eficiente
- Uso de procedimientos en VHDL: Simplifique su diseño con código reutilizable
- Construya un temporizador fiable en VHDL: Contar ciclos de reloj hasta horas
- Construcción de un proceso activado por reloj en VHDL: Guía práctica
- Dominando la instanciación con port map en VHDL: Guía práctica