Como usar um procedimento em um processo em VHDL
É possível conduzir sinais externos de um procedimento. Desde que o sinal esteja dentro do escopo do procedimento, ele pode ser acessado para leitura ou escrita, mesmo que não esteja listado na lista de parâmetros.
Os procedimentos que são declarados na região declarativa da arquitetura não podem conduzir nenhum sinal externo. Isso ocorre simplesmente porque não há sinais em seu escopo em tempo de compilação. Um procedimento declarado dentro de um processo, por outro lado, terá acesso a todos os sinais que o processo pode ver.
Esta postagem do blog faz parte da série de tutoriais básicos de VHDL.
Tais procedimentos podem ser usados para organizar algoritmos em processos onde as mesmas operações ocorrem várias vezes. Poderíamos usar um procedimento normal onde todas as entradas e saídas são atribuídas a sinais locais quando você os chama, mas esse não é o ponto. Ao omitir os sinais de entrada e saída da chamada do procedimento, devemos digitar menos e, mais importante, tornar o código mais legível.
Imagine um processo implementando um protocolo de comunicação complexo. Seria muito mais fácil entender o fluxo de execução do algoritmo principal se algumas operações fossem substituídas por chamadas de procedimento como
RequestToSend()
ou SendAutorizationHeader()
. Você saberia o que essas linhas faziam apenas olhando os nomes dos procedimentos. Exercício
No tutorial anterior, simplificamos nosso código de máquina de estado finito (FSM) usando uma função impura. Estávamos dirigindo o
Counter
sinal da função impura e usamos o valor de retorno para determinar quando mudar de estado. Mas e se quisermos mover a atribuição do State
sinal na função também e ignorar o valor de retorno? Não é possível chamar uma função sem atribuir o valor de retorno a algo em VHDL. Se tentarmos fazer isso, o ModelSim produzirá o erro de compilação: Nenhuma entrada viável para o subprograma “CounterExpired”.
Em vez disso, podemos usar um procedimento para isso. Um procedimento declarado dentro de um processo pode acessar qualquer sinal dentro do escopo desse processo. Isso é semelhante à função impura, mas como é um procedimento, não há valor de retorno.
Neste tutorial em vídeo vamos simplificar o código FSM usando um procedimento declarado em um processo:
O código final para o procedimento em processo testbench :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T23_ProcedureInProcessTb is end entity; architecture sim of T23_ProcedureInProcessTb 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.T23_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 para o módulo de semáforos :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T23_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 T23_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; -- Counter for counting clock periods, 1 minute max signal Counter : integer range 0 to ClockFrequencyHz * 60; begin process(Clk) is -- Procedure for changing state after a given time procedure ChangeState(ToState : t_State; Minutes : integer := 0; Seconds : integer := 0) is variable TotalSeconds : integer; variable ClockCycles : integer; begin TotalSeconds := Seconds + Minutes * 60; ClockCycles := TotalSeconds * ClockFrequencyHz -1; if Counter = ClockCycles then Counter <= 0; State <= ToState; end if; end procedure; 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'; ChangeState(StartNorth, Seconds => 5); -- Red and yellow in north/south direction when StartNorth => NorthRed <= '1'; NorthYellow <= '1'; WestRed <= '1'; ChangeState(North, Seconds => 5); -- Green in north/south direction when North => NorthGreen <= '1'; WestRed <= '1'; ChangeState(StopNorth, Minutes => 1); -- Yellow in north/south direction when StopNorth => NorthYellow <= '1'; WestRed <= '1'; ChangeState(WestNext, Seconds => 5); -- Red in all directions when WestNext => NorthRed <= '1'; WestRed <= '1'; ChangeState(StartWest, Seconds => 5); -- Red and yellow in west/east direction when StartWest => NorthRed <= '1'; WestRed <= '1'; WestYellow <= '1'; ChangeState(West, Seconds => 5); -- Green in west/east direction when West => NorthRed <= '1'; WestGreen <= '1'; ChangeState(StopWest, Minutes => 1); -- Yellow in west/east direction when StopWest => NorthRed <= '1'; WestYellow <= '1'; ChangeState(NorthNext, Seconds => 5); 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
Não alteramos o comportamento do módulo e podemos ver que a forma de onda permanece inalterada.
Comparado com o código do tutorial onde criamos inicialmente o módulo de semáforos, o código FSM está muito mais legível agora. Você pode facilmente seguir o algoritmo que ele implementa lendo o código. Ter o temporizador e a lógica de mudança de estado em um único procedimento é benéfico porque garante que seja implementado igualmente em todos os lugares em que for usado.
Retirada
- Um procedimento declarado em um processo pode acessar qualquer sinal dentro do escopo desse processo
- Procedimentos dentro de processos podem ser usados para melhorar a legibilidade do código
VHDL
- Tutorial - Introdução ao VHDL
- Declaração de Procedimento - Exemplo de VHDL
- Como usamos o molibdênio?
- Como criar uma lista de strings em VHDL
- Como usar uma função impura em VHDL
- Como usar uma função em VHDL
- Como usar um procedimento em VHDL
- Como criar um temporizador em VHDL
- Como criar um processo cronometrado em VHDL
- Como usar um moedor de corte