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
waitdeclaraçõ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