Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial programming >> VHDL

Dominando funciones VHDL: Guía práctica para diseño eficiente

Las funciones son subprogramas en VHDL que pueden usarse para implementar algoritmos usados frecuentemente. Una función toma cero o más valores de entrada, y siempre retorna un valor. Además del valor de retorno, lo que distingue a una función de un procedimiento es que no puede contener sentencias Wait. 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 de retorno o retornar void, una función siempre tiene que retornar algo y el valor de retorno tiene que ser asignado a algo.

Esta entrada de blog es parte de la serie Tutoriales Básicos de VHDL.

En VHDL, hay dos tipos de funciones, puras e impuras. Que una función sea pura significa que no se le permitirá modificar o leer ninguna señal externa. Podemos estar seguros de que cuando llamamos a una función pura con ciertos argumentos, siempre retornará 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 pure/impure es opcional, aunque por defecto será pure si se omite la palabra clave. Todos los parámetros se tratan como constantes dentro de la función. Por lo tanto, no pueden ser cambiados. Los valores por defecto son opcionales, y la función siempre debe terminar en una sentencia return.

Las funciones tienen su propia región declarativa entre las palabras clave is y begin. Constantes, señales o variables declaradas aquí son válidas solo dentro de la función misma, y no retendrán sus valores a través de llamadas subsiguientes a la función.

Ejercicio

En este tutorial, nos centraremos en la función pura; las funciones impuras se cubrirá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 estados finitos (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 estados usando una función:

El código final para el testbench de la función:

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
  -- Usamos una frecuencia de reloj baja para acelerar la simulación
  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
  -- 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
    );
  -- Proceso para generar reloj
  Clk <= not Clk after ClockPeriod / 2;
  -- Secuencia testbench
  process is
  begin
    wait until rising_edge(Clk);
    wait until rising_edge(Clk);
    -- Sacar DUT de 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; -- Reset negativo
    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
  -- Declaración de tipo enumerado y señal de estado
  type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest);
  signal State : t_State;
  
  -- Calcular número de ciclos de reloj en minutos/segundos
  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;
  
  -- Contador para contar períodos de reloj, 1 minuto máximo
  signal Counter : integer range 0 to CounterVal(Minutes => 1) + 1;
  
begin
  process(Clk) is
  begin
    if rising_edge(Clk) then
      if nRst = '0' then
        -- Valores de reset
        NorthRed   <= '1';
        NorthYellow<= '0';
        NorthGreen <= '0';
        WestRed    <= '1';
        WestYellow <= '0';
        WestGreen  <= '0';
        State      <= NorthNext;
        Counter    <= 0;
      else
        -- Valores por defecto
        NorthRed   <= '0';
        NorthYellow<= '0';
        NorthGreen <= '0';
        WestRed    <= '0';
        WestYellow <= '0';
        WestGreen  <= '0';
        Counter    <= Counter + 1;
        
        case State is
          -- Luz roja en todas direcciones
          when NorthNext =>
            NorthRed <= '1';
            WestRed  <= '1';
            -- Si han pasado 5 segundos
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State   <= StartNorth;
            end if;
          -- Luz amarilla en direcciones norte/sur
          when StartNorth =>
            NorthRed   <= '1';
            NorthYellow<= '1';
            WestRed    <= '1';
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State   <= North;
            end if;
          -- Luz verde en direcciones norte/sur
          when North =>
            NorthGreen <= '1';
            WestRed    <= '1';
            if Counter = CounterVal(Minutes => 1) then
              Counter <= 0;
              State   <= StopNorth;
            end if;
          -- Luz roja y amarilla en dirección norte/sur
          when StopNorth =>
            NorthYellow<= '1';
            WestRed    <= '1';
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State   <= WestNext;
            end if;
          -- Luz roja en todas direcciones
          when WestNext =>
            NorthRed <= '1';
            WestRed  <= '1';
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State   <= StartWest;
            end if;
          -- Luz amarilla en dirección oeste/este
          when StartWest =>
            NorthRed   <= '1';
            WestRed    <= '1';
            WestYellow <= '1';
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State   <= West;
            end if;
          -- Luz verde en dirección oeste/este
          when West =>
            NorthRed  <= '1';
            WestGreen <= '1';
            if Counter = CounterVal(Minutes => 1) then
              Counter <= 0;
              State   <= StopWest;
            end if;
          -- Luz roja y amarilla en dirección oeste/este
          when StopWest =>
            NorthRed   <= '1';
            WestYellow <= '1';
            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 ingresar el comando run 5 min en la consola de ModelSim:
Dominando funciones VHDL: Guía práctica para diseño eficiente

La forma de onda con cursores añadidos en las transiciones hacia y desde el estado StartNorth:
Dominando funciones VHDL: Guía práctica para diseño eficiente

Análisis

Reemplazamos los cálculos de temporizador del tutorial anterior if Counter = ClockFrequencyHz * 5 -1 then con una llamada a la nueva función CounterVal que creamos: if Counter = CounterVal(Seconds => 5) then.

Podemos ver en la primera captura de pantalla de la forma de onda que la función del módulo no ha cambiado. Usar funciones para tareas repetitivas es una buena práctica de diseño. Especialmente si puede reemplazar cálculos con líneas más legibles que contienen 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 la función CounterVal, todos los temporizadores habrían durado un ciclo de reloj demasiado. Podríamos entonces cambiar esto a return TotalSeconds * ClockFrequencyHz -1; en la función CounterVal, y todos los temporizadores se arreglarían a la vez.

Si examinamos la última captura de pantalla de la forma de onda, podemos ver por qué necesitamos restar 1 del valor del temporizador que retorna la función CounterVal. Esta forma de onda examina la duración del estado StartNorth, que debería durar exactamente cinco segundos. Cuando la señal State cambia a StartNorth, el valor Counter es 0, y solo cambia después del próximo ciclo de reloj. Así, si hubiéramos contado hasta 500 ciclos de reloj, el estado StartNorth habría durado realmente 501 ciclos. Con nuestro testbench corriendo a 100 Hz, 500 ciclos de reloj son exactamente cinco segundos.

Dominando funciones VHDL: Guía práctica para diseño eficiente

Conclusión

Vaya al siguiente tutorial »

VHDL

  1. Dominando las Técnicas de Afiladora de Herramientas: Guía Práctica
  2. Cómo el molibdeno moldea la industria moderna: usos, beneficios y dinámica de suministro
  3. Uso de realloc() en C: Sintaxis, mejores prácticas y ejemplo
  4. Dominando la función free() en C: Guía práctica y ejemplo
  5. Creación de listas de cadenas en VHDL: Buenas prácticas y ejemplo
  6. Finalizar elegantemente una simulación de testbench VHDL
  7. Implementación de un controlador PWM en VHDL: Diseño, simulación y demo en FPGA
  8. Generación de números aleatorios en VHDL: De uniforme a OSVVM
  9. Aprovechando procedimientos en-proceso para diseños FSM más limpios en VHDL
  10. Uso de funciones impuras en VHDL: Mejorando legibilidad y mantenibilidad de FSM
  11. Diseño de máquina de estados finitos en VHDL: Ejemplo práctico de semáforo