Cómo usar una función en VHDL
Las funciones son subprogramas en VHDL que se pueden usar para implementar algoritmos de uso frecuente. Una función toma cero o más valores de entrada y siempre devuelve un valor. Además del valor devuelto, lo que distingue a una función de un procedimiento es que no puede contener declaraciones de espera. Esto significa que las funciones siempre consumen tiempo de simulación cero.
Si está familiarizado con funciones o métodos de otros lenguajes de programación, las funciones VHDL deberían ser fáciles de entender. En VHDL no podemos omitir el valor devuelto ni devolver vacío, una función siempre tiene que devolver algo y el valor devuelto tiene que asignarse a algo.
Esta publicación de blog es parte de la serie Tutoriales básicos de VHDL.
En VHDL, hay dos tipos de funciones, puras e impuro funciones Que una función sea pura significa que no se le permitirá modificar ni leer ninguna señal externa. Podemos estar seguros de que cuando llamamos a una función pura con ciertos argumentos, siempre devolverá el mismo valor. Decimos que la función no tiene efectos secundarios .
La sintaxis para declarar una función en VHDL es:
[pure|impure] function <function_name> (<parameter1_name> : <parameter1_type> := <default_value>;
<parameter2_name> : <parameter2_type> := <default_value>;
... ) return <return_type> is
<constant_or_variable_declaration>
begin
<code_performed_by_the_function>
return <value>
end function;
La palabra clave puro/impuro es opcional, aunque por defecto será puro si se omite la palabra clave. Todos los parámetros se tratan como constantes dentro de la función. Por lo tanto, no se pueden cambiar. Los valores predeterminados son opcionales y la función siempre debe terminar en un return declaración.
Las funciones tienen su propia región declarativa entre el in y begin palabras clave Las constantes, señales o variables declaradas aquí son válidas solo dentro de la función misma y no conservarán sus valores a través de llamadas posteriores a la función.
Ejercicio
En este tutorial, nos centraremos en la función pura, las funciones impuras se tratarán en un tutorial posterior de esta serie.
En el tutorial anterior, creamos un módulo controlador de semáforos usando una máquina de estado finito (FSM). Copiamos y pegamos muchas de las líneas que contenían cálculos de temporizador de un estado a otro, cambiando solo una constante ligeramente.
Descubra cómo puede simplificar el código de la máquina de estado usando una función:
El código final para la función testbench :
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T21_FunctionTb is
end entity;
architecture sim of T21_FunctionTb 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.T21_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 T21_TrafficLights is
generic(ClockFrequencyHz : natural);
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 T21_TrafficLights is
-- Enumerated type declaration and state signal declaration
type t_State is (NorthNext, StartNorth, North, StopNorth,
WestNext, StartWest, West, StopWest);
signal State : t_State;
-- 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;
-- Counter for counting clock periods, 1 minute max
signal Counter : integer range 0 to CounterVal(Minutes => 1) +1;
begin
process(Clk) is
begin
if rising_edge(Clk) then
if nRst = '0' then
-- Reset values
NorthRed <= '1';
NorthYellow <= '0';
NorthGreen <= '0';
WestRed <= '1';
WestYellow <= '0';
WestGreen <= '0';
State <= NorthNext;
Counter <= 0;
else
-- Default values
NorthRed <= '0';
NorthYellow <= '0';
NorthGreen <= '0';
WestRed <= '0';
WestYellow <= '0';
WestGreen <= '0';
Counter <= Counter + 1;
case State is
-- Red light in all directions
when NorthNext =>
NorthRed <= '1';
WestRed <= '1';
-- If 5 seconds have passed
if Counter = CounterVal(Seconds => 5) then
Counter <= 0;
State <= StartNorth;
end if;
-- Yellow light in north/south directions
when StartNorth =>
NorthRed <= '1';
NorthYellow <= '1';
WestRed <= '1';
-- If 5 seconds have passed
if Counter = CounterVal(Seconds => 5) then
Counter <= 0;
State <= North;
end if;
-- Green light in north/south directions
when North =>
NorthGreen <= '1';
WestRed <= '1';
-- If 1 minute has passed
if Counter = CounterVal(Minutes => 1) then
Counter <= 0;
State <= StopNorth;
end if;
-- Red and yellow light in north/south direction
when StopNorth =>
NorthYellow <= '1';
WestRed <= '1';
-- If 5 seconds have passed
if Counter = CounterVal(Seconds => 5) then
Counter <= 0;
State <= WestNext;
end if;
-- Red light in all directions
when WestNext =>
NorthRed <= '1';
WestRed <= '1';
-- If 5 seconds have passedf
if Counter = CounterVal(Seconds => 5) then
Counter <= 0;
State <= StartWest;
end if;
-- Yellow light in west/east direction
when StartWest =>
NorthRed <= '1';
WestRed <= '1';
WestYellow <= '1';
-- If 5 seconds have passed
if Counter = CounterVal(Seconds => 5) then
Counter <= 0;
State <= West;
end if;
-- Green light in west/east direction
when West =>
NorthRed <= '1';
WestGreen <= '1';
-- If 1 minute has passed
if Counter = CounterVal(Minutes => 1) then
Counter <= 0;
State <= StopWest;
end if;
-- Red and yellow light in west/east direction
when StopWest =>
NorthRed <= '1';
WestYellow <= '1';
-- If 5 seconds have passed
if Counter = CounterVal(Seconds => 5) then
Counter <= 0;
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:
La forma de onda con cursores agregados en las transiciones hacia y desde el StartNorth estado:
Análisis
Reemplazamos los cálculos del temporizador del tutorial anterior if Counter = ClockFrequencyHz * 5 -1 then con una llamada al nuevo CounterVal función que creamos:if Counter = CounterVal(Seconds => 5) then .
Podemos ver en la primera captura de pantalla de forma de onda que la función del módulo no ha cambiado. El uso de funciones para tareas repetitivas es una buena práctica de diseño. Especialmente si puede reemplazar los cálculos con líneas más legibles que contengan términos como Minutes y Seconds .
Otra ventaja de usar funciones es que podemos cambiar la implementación de todos los temporizadores a la vez, en lugar de hacerlo línea por línea. Por ejemplo, si hubiéramos escrito return TotalSeconds * ClockFrequencyHz; en el CounterVal función, todos los temporizadores habrían durado un ciclo de reloj demasiado tiempo. Entonces podríamos cambiar esto a return TotalSeconds * ClockFrequencyHz -1; en el CounterVal y todos los temporizadores se arreglarían a la vez.
Si examinamos la captura de pantalla de la última forma de onda, podemos ver por qué necesitamos restar 1 del valor del temporizador que se devuelve del CounterVal función. Esta forma de onda examina la duración del StartNorth estado, debería durar exactamente cinco segundos. Cuando el State la señal cambia a StartNorth , el Counter el valor es 0, y solo cambia después del próximo ciclo de reloj. Entonces, si hubiéramos contado hasta 500 ciclos de reloj, el StartNorth estado hubiera durado realmente 501 ciclos. Con nuestro banco de pruebas funcionando a 100 Hz, 500 ciclos de reloj son exactamente cinco segundos.
Para llevar
- Las funciones pueden tomar cero o más parámetros, pero siempre devuelven un valor
- Las funciones no pueden contener
waitdeclaraciones - Las funciones puras no pueden tener efectos secundarios, mientras que las funciones impuras sí.
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 impura 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