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

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

Ir al siguiente tutorial »


VHDL

  1. ¿Cómo utilizamos el molibdeno?
  2. Cómo crear una lista de cadenas en VHDL
  3. Cómo detener la simulación en un banco de pruebas VHDL
  4. Cómo crear un controlador PWM en VHDL
  5. Cómo generar números aleatorios en VHDL
  6. Cómo usar un procedimiento en un proceso en VHDL
  7. Cómo usar una función en VHDL
  8. Cómo crear una máquina de estados finitos en VHDL
  9. Realloc () Función en la biblioteca C:¿Cómo usar? Sintaxis y ejemplo
  10. Función free () en la biblioteca C:¿Cómo usar? Aprende con el Ejemplo
  11. Cómo usar una amoladora cortadora