Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial programming >> VHDL

Como usar uma função impura em VHDL


Uma função impura pode ler ou escrever qualquer sinal dentro de seu escopo, inclusive aqueles que não estão na lista de parâmetros. Dizemos que a função tem efeitos colaterais .

O que queremos dizer com efeitos colaterais é que não é garantido que a função retornará o mesmo valor toda vez que for chamada com os mesmos parâmetros. Se a função pode ler sinais que não estão na lista de parâmetros, o valor de retorno também pode depender desses parâmetros de sombra. Além disso, a função pode estar alterando sinais externos que não são atribuídos a partir de seu valor de retorno.

Esta postagem do blog faz parte da série de tutoriais básicos de VHDL.

Embora possamos declarar funções impuras em qualquer lugar em que possamos declarar uma função normal e pura, só faz sentido usá-las dentro de processos. Quando declarado na arquitetura onde normalmente declaramos nossos sinais, nenhum dos sinais estará em seu escopo em tempo de compilação. Assim, uma função impura não pode fazer nada mais do que uma função pura pode quando declarada na arquitetura ou dentro de um pacote.

A motivação para o uso de funções impuras é principalmente a organização do código. Poderíamos manipular qualquer sinal com uma função pura simplesmente adicionando-o à lista de parâmetros, mas se a lista de parâmetros se tornar muito longa, ofuscará em vez de simplificar.

A sintaxe para declarar uma função impura é simplesmente escrever impure function em vez de function ao declará-lo. Consulte o tutorial de função para obter a sintaxe de uma função genérica.

Exercício


No tutorial anterior, simplificamos nosso código de máquina de estado finito (FSM) usando uma função para calcular valores de atraso de tempo. Fornecemos os parâmetros Minutes e Seconds para especificar por quanto tempo queríamos atrasar cada mudança de estado.

Se o CounterVal função retornou true , o tempo havia expirado e era hora de ir para o próximo estado FSM. No mesmo processo, também tivemos que redefinir o Counter sinal, caso contrário, a função não funcionaria no próximo estado. O cronômetro já teria expirado.

O Counter sinal sempre seria definido como 0 quando a função retornou true. Não seria melhor se isso acontecesse no CounterVal função em vez de vários lugares no código de máquina de estado?

Neste tutorial em vídeo vamos melhorar o código FSM do tutorial anterior usando uma função impura:



O código final para a função 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;

O código final do módulo do semáforo :
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;

A forma de onda depois de inserirmos o run 5 min comando no console ModelSim:





Análise


Como podemos ver na forma de onda, a saída do módulo permanece inalterada depois que adicionamos a função impura. Nós não mudamos a lógica, apenas o código.

A avaliação do Counter sinal foi movido do código FSM para a nova função impura CounterExpired . O Counter <= 0; linha para limpar o Counter sinal também foi movido para a função impura.

O resultado é um código FSM mais legível que pode ser mantido mais facilmente. Isso é subjetivo, mas para mim CounterExpired(Seconds => 5) é mais agradável aos olhos do que Counter = CounterVal(Seconds => 5) .

Até onde você deve ir com o uso de funções impuras depende inteiramente de você e de quem paga por seus serviços. Algumas pessoas acham que devem ser usadas com cautela porque pode ser mais difícil ver todas as causas e efeitos de um algoritmo oculto em um subprograma. Outros, como eu, acham que, desde que você deixe suas intenções claras, quanto mais fácil de ler o código, na verdade, ele fica menos propenso a erros.

Por esse motivo, é mais provável que você encontre funções impuras no código do testbench do que nos módulos de produção. Os testbenches são normalmente mais complexos do que o módulo que estão testando, e o requisito de correção do código é menos rigoroso do que para o código RTL.


Retirada


Ir para o próximo tutorial »

VHDL

  1. Como usamos o molibdênio?
  2. Como criar uma lista de strings em VHDL
  3. Como parar a simulação em um testbench VHDL
  4. Como criar um controlador PWM em VHDL
  5. Como gerar números aleatórios em VHDL
  6. Como usar um procedimento em um processo em VHDL
  7. Como usar uma função em VHDL
  8. Função realloc() na Biblioteca C:Como usar? Sintaxe e Exemplo
  9. Função free() na biblioteca C:Como usar? Aprenda com o Exemplo
  10. Como usar um moedor de corte