Como usar uma função em VHDL
Funções são subprogramas em VHDL que podem ser usados para implementar algoritmos usados com frequência. Uma função recebe zero ou mais valores de entrada e sempre retorna um valor. Além do valor de retorno, o que diferencia uma função de um procedimento é que ela não pode conter instruções Wait. Isso significa que as funções sempre consomem tempo de simulação zero.
Se você estiver familiarizado com funções ou métodos de outras linguagens de programação, as funções VHDL devem ser fáceis de entender. Em VHDL não podemos omitir o valor de retorno ou retornar void, uma função sempre tem que retornar algo e o valor de retorno tem que ser atribuído a algo.
Esta postagem do blog faz parte da série de tutoriais básicos de VHDL.
Em VHDL, existem dois tipos de funções, puras e impuro funções. O fato de uma função ser pura significa que ela não poderá modificar ou ler nenhum sinal externo. Podemos ter certeza de que quando chamamos uma função pura com certos argumentos, ela sempre retornará o mesmo valor. Dizemos que a função não tem efeitos colaterais .
A sintaxe para declarar uma função em VHDL é:
[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;
A palavra-chave pure/impure é opcional, embora o padrão seja pure se a palavra-chave for omitida. Todos os parâmetros são tratados como constantes dentro da função. Assim, eles não podem ser alterados. Os valores padrão são opcionais e a função deve sempre terminar em um
return
declaração. As funções têm sua própria região declarativa entre o
in
e begin
palavras-chave. Constantes, sinais ou variáveis declarados aqui são válidos apenas dentro da própria função e não reterão seus valores por meio de chamadas subsequentes à função. Exercício
Neste tutorial, vamos nos concentrar na função pura, as funções impuras serão abordadas em um tutorial posterior desta série.
No tutorial anterior, criamos um módulo controlador de semáforos usando uma máquina de estado finito (FSM). Copiamos e colamos muitas das linhas contendo cálculos de temporizador de um estado para outro, alterando apenas uma constante ligeiramente.
Descubra como você pode simplificar o código da máquina de estado usando uma função:
O código final para a função 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;
O código final do módulo do semáforo :
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;
A forma de onda depois de inserirmos o
run 5 min
comando no console ModelSim:A forma de onda com cursores adicionados nas transições de e para o
StartNorth
estado:Análise
Substituímos os cálculos do temporizador do tutorial anterior
if Counter = ClockFrequencyHz * 5 -1 then
com uma chamada para o novo CounterVal
função que criamos:if Counter = CounterVal(Seconds => 5) then
. Podemos ver na primeira captura de tela da forma de onda que a função do módulo não foi alterada. Usar funções para tarefas repetitivas é uma boa prática de design. Especialmente se você puder substituir cálculos por linhas mais legíveis contendo termos como
Minutes
e Seconds
. Outra vantagem de usar funções é que podemos alterar a implementação de todos os temporizadores de uma só vez, em vez de fazê-lo linha por linha. Por exemplo, se tivéssemos escrito
return TotalSeconds * ClockFrequencyHz;
no CounterVal
função, todos os temporizadores teriam durado um ciclo de clock muito longo. Poderíamos então mudar isso para return TotalSeconds * ClockFrequencyHz -1;
no CounterVal
função, e todos os temporizadores seriam fixados de uma só vez. Se examinarmos a última captura de tela da forma de onda, podemos ver por que precisamos subtrair 1 do valor do temporizador que é retornado do
CounterVal
função. Esta forma de onda examina a duração do StartNorth
estado, deve durar exatamente cinco segundos. Quando o State
o sinal muda para StartNorth
, o Counter
valor é 0, e só muda após o próximo ciclo de clock. Então, se tivéssemos contado até 500 ciclos de clock, o StartNorth
estado teria realmente durado 501 ciclos. Com nosso testbench rodando a 100 Hz, 500 ciclos de clock são exatamente cinco segundos. Retirada
- As funções podem receber zero ou mais parâmetros, mas sempre retornam um valor
- As funções não podem conter
wait
declarações - Funções puras não podem ter efeitos colaterais, enquanto funções impuras podem.
Ir para o próximo tutorial »
VHDL
- Como usamos o molibdênio?
- Como criar uma lista de strings em VHDL
- Como parar a simulação em um testbench VHDL
- Como criar um controlador PWM em VHDL
- Como gerar números aleatórios em VHDL
- Como usar um procedimento em um processo em VHDL
- Como usar uma função impura em VHDL
- Função realloc() na Biblioteca C:Como usar? Sintaxe e Exemplo
- Função free() na biblioteca C:Como usar? Aprenda com o Exemplo
- Como usar um moedor de corte