Cómo usar una función impura en VHDL
Una función impura puede leer o escribir cualquier señal dentro de su alcance, también aquellas que no están en la lista de parámetros. Decimos que la función tiene efectos secundarios .
Lo que queremos decir con efectos secundarios es que no se garantiza que la función devuelva el mismo valor cada vez que se llame con los mismos parámetros. Si la función puede leer señales que no están en la lista de parámetros, el valor devuelto también puede depender de estos parámetros de sombra. Además, la función puede estar alterando señales externas que no están asignadas desde su valor de retorno.
Esta publicación de blog es parte de la serie Tutoriales básicos de VHDL.
Aunque podemos declarar funciones impuras en cualquier lugar, podemos declarar una función pura normal, solo tiene sentido usarlas dentro de los procesos. Cuando se declara en la arquitectura donde normalmente declaramos nuestras señales, ninguna de las señales estará en su alcance en el momento de la compilación. Por lo tanto, una función impura no puede hacer nada más que una función pura cuando se declara en la arquitectura o dentro de un paquete.
La motivación para usar funciones impuras es principalmente despejar el código. Podríamos manipular cualquier señal con una función pura simplemente agregándola a la lista de parámetros, pero si la lista de parámetros se vuelve demasiado larga, se ofuscaría en lugar de simplificarla.
La sintaxis para declarar una función impura es simplemente escribir impure function
en lugar de function
al declararlo. Consulte el tutorial de funciones para conocer la sintaxis de una función genérica.
Ejercicio
En el tutorial anterior, simplificamos nuestro código de máquina de estado finito (FSM) mediante el uso de una función para calcular valores de retardo de tiempo. Proporcionamos los parámetros Minutos y Segundos para especificar cuánto tiempo queríamos retrasar cada cambio de estado.
Si el CounterVal
la función devolvió true
, el tiempo había expirado y era hora de pasar al siguiente estado de FSM. En el mismo proceso, también tuvimos que restablecer el Counter
señal, de lo contrario, la función no funcionaría en el siguiente estado. El temporizador ya estaría vencido.
El Counter
la señal siempre se establecería en 0
cuando la función devolvió verdadero. ¿No sería mejor si esto sucediera en el CounterVal
función en lugar de múltiples lugares en el código de máquina de estado?
En este video tutorial mejoraremos el código FSM del tutorial anterior usando una función impura:
El código final para la función impura testbench :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T22_ImpureFunctionTb is end entity; architecture sim of T22_ImpureFunctionTb is -- We are using a low clock frequency to speed up the simulation 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 -- The Device Under Test (DUT) i_TrafficLights : entity work.T22_TrafficLights(rtl) generic map(ClockFrequencyHz => ClockFrequencyHz) port map ( Clk => Clk, nRst => nRst, NorthRed => NorthRed, NorthYellow => NorthYellow, NorthGreen => NorthGreen, WestRed => WestRed, WestYellow => WestYellow, WestGreen => WestGreen); -- Process for generating clock Clk <= not Clk after ClockPeriod / 2; -- Testbench sequence process is begin wait until rising_edge(Clk); wait until rising_edge(Clk); -- Take the DUT out of reset nRst <= '1'; wait; end process; end architecture;
El código final para el módulo de semáforos :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T22_TrafficLights is generic(ClockFrequencyHz : integer); port( Clk : in std_logic; nRst : in std_logic; -- Negative reset 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 T22_TrafficLights is -- Calculate the number of clock cycles in minutes/seconds function CounterVal(Minutes : integer := 0; Seconds : integer := 0) return integer is variable TotalSeconds : integer; begin TotalSeconds := Seconds + Minutes * 60; return TotalSeconds * ClockFrequencyHz -1; end function; -- Enumerated type declaration and state signal declaration type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest); signal State : t_State; -- Counter for counting clock periods, 1 minute max signal Counter : integer range 0 to ClockFrequencyHz * 60; begin process(Clk) is -- This impure function reads and drives the Counter signal -- which is not on the parameter list. impure function CounterExpired(Minutes : integer := 0; Seconds : integer := 0) return boolean is begin if Counter = CounterVal(Minutes, Seconds) then Counter <= 0; return true; else return false; end if; end function; begin if rising_edge(Clk) then if nRst = '0' then -- Reset values State <= NorthNext; Counter <= 0; NorthRed <= '1'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '1'; WestYellow <= '0'; WestGreen <= '0'; else -- Default values NorthRed <= '0'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '0'; WestYellow <= '0'; WestGreen <= '0'; Counter <= Counter + 1; case State is -- Red in all directions when NorthNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= StartNorth; end if; -- Red and yellow in north/south direction when StartNorth => NorthRed <= '1'; NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= North; end if; -- Green in north/south direction when North => NorthGreen <= '1'; WestRed <= '1'; -- If 1 minute has passed if CounterExpired(Minutes => 1) then State <= StopNorth; end if; -- Yellow in north/south direction when StopNorth => NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= WestNext; end if; -- Red in all directions when WestNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= StartWest; end if; -- Red and yellow in west/east direction when StartWest => NorthRed <= '1'; WestRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= West; end if; -- Green in west/east direction when West => NorthRed <= '1'; WestGreen <= '1'; -- If 1 minute has passed if CounterExpired(Minutes => 1) then State <= StopWest; end if; -- Yellow in west/east direction when StopWest => NorthRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= NorthNext; end if; end case; end if; end if; end process; end architecture;
La forma de onda después de que ingresamos el run 5 min
comando en la consola de ModelSim:
Análisis
Como podemos ver en la forma de onda, la salida del módulo permanece sin cambios después de agregar la función impura. No hemos cambiado la lógica en absoluto, solo el código.
La evaluación del Counter
la señal se ha movido del código FSM a la nueva función impura CounterExpired
. El Counter <= 0;
línea para borrar el Counter
la señal también se ha movido a la función impura.
El resultado es un código FSM más legible que se puede mantener más fácilmente. Esto es subjetivo, pero para mí CounterExpired(Seconds => 5)
es más agradable a la vista que Counter = CounterVal(Seconds => 5)
.
Hasta dónde debe llegar con el uso de funciones impuras depende totalmente de usted y de quien pague por sus servicios. Algunas personas sienten que deben usarse con precaución porque puede ser más difícil ver a través de todas las causas y efectos de un algoritmo oculto en un subprograma. Otros, como yo, sienten que siempre que deje claras sus intenciones, el código más fácil de leer en realidad lo hace menos propenso a errores.
Por esta razón, es más probable que encuentre funciones impuras en el código del banco de pruebas que en los módulos de producción. Los bancos de pruebas suelen ser más complejos que el módulo que están probando, y el requisito de corrección del código es menos estricto que el del código RTL.
Para llevar
- Las funciones impuras pueden leer o controlar señales que no están en su lista de parámetros
- Solo tiene sentido declarar funciones impuras dentro de un proceso
Ir al siguiente tutorial »
VHDL
- ¿Cómo utilizamos el molibdeno?
- Cómo crear una lista de cadenas en VHDL
- Cómo detener la simulación en un banco de pruebas VHDL
- Cómo crear un controlador PWM en VHDL
- Cómo generar números aleatorios en VHDL
- Cómo usar un procedimiento en un proceso en VHDL
- Cómo usar una función en VHDL
- Cómo crear una máquina de estados finitos en VHDL
- Realloc () Función en la biblioteca C:¿Cómo usar? Sintaxis y ejemplo
- Función free () en la biblioteca C:¿Cómo usar? Aprende con el Ejemplo
- Cómo usar una amoladora cortadora