Como criar uma máquina de estado finito em VHDL
Uma máquina de estado finito (FSM) é um mecanismo cuja saída depende não apenas do estado atual da entrada, mas também dos valores passados de entrada e saída.
Sempre que você precisar criar algum tipo de algoritmo dependente do tempo em VHDL, ou se você se deparar com o problema de implementar um programa de computador em um FPGA, ele geralmente pode ser resolvido usando um FSM.
Máquinas de estado em VHDL são processos cronometrados cujas saídas são controladas pelo valor de um sinal de estado. O sinal de estado serve como uma memória interna do que aconteceu na iteração anterior.
Esta postagem do blog faz parte da série de tutoriais básicos de VHDL.
Considere os estados dos semáforos neste cruzamento:
Os semáforos têm um número finito de estados, aos quais demos nomes identificáveis. Nossa máquina de estado de exemplo não tem entradas de controle, a saída é o estado das luzes nas direções norte/sul e oeste/leste. É o tempo decorrido e o estado anterior das saídas que avança esta máquina de estados.
Podemos representar estados em VHDL usando um tipo enumerado . Estes são tipos de dados como
signed
ou unsigned
, mas em vez de números inteiros, podemos fornecer uma lista personalizada de valores possíveis. Na verdade, se você der uma olhada no pacote std_logic_1164, verá que o std_ulogic
type nada mais é do que um tipo enumerado com os valores 'U'
, 'X'
, '0'
, '1'
, 'Z'
, 'W'
, 'L'
, 'H'
e '-'
listados como valores de enumeração. Uma vez que tenhamos nosso tipo enumerado, podemos declarar um sinal do novo tipo que pode ser usado para acompanhar o estado atual do FSM.
A sintaxe para declarar um sinal com um tipo enumerado em VHDL é:
type <type_name> is (<state_name1>, <state_name2>, ...);
signal <signal_name> : <type_name>;
Usando o sinal de estado, a máquina de estado finito pode ser implementada em um processo com uma instrução Case. A instrução Case contém uma instrução When para cada um dos estados possíveis, fazendo com que o programa tome caminhos diferentes para cada estado. A instrução When também pode conter código que deve ser executado enquanto estiver nesse estado específico. O estado normalmente mudará quando uma condição predefinida for atendida.
Este é um modelo para máquina de estado de um processo:
process(Clk) is
begin
if rising_edge(Clk) then
if nRst = '0' then
State <= <reset_state>;
else
case State is
when <state_name> =>
<set_outputs_for_this_state_here>
if <state_change_condition_is_true> then
State <= <next_state_name>;
end if;
...
end case;
end if;
end if;
end process;
Nota:
Existem várias maneiras de criar um FSM em VHDL. Leia sobre os diferentes estilos aqui:
Máquina de estado de um processo versus dois processos versus três processos
Exercício
Neste tutorial em vídeo vamos aprender como criar uma máquina de estado finito em VHDL:
O código final para a máquina de estado testbench :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T20_FiniteStateMachineTb is end entity; architecture sim of T20_FiniteStateMachineTb 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.T20_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 a máquina de estado módulo :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T20_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 T20_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 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 Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; 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 Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= North; end if; -- Green in north/south direction when North => NorthGreen <= '1'; WestRed <= '1'; -- If 1 minute has passed if Counter = ClockFrequencyHz * 60 -1 then Counter <= 0; State <= StopNorth; end if; -- Yellow in north/south direction when StopNorth => NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= WestNext; end if; -- Red in all directions when WestNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passed if Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; 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 Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= West; end if; -- Green in west/east direction when West => NorthRed <= '1'; WestGreen <= '1'; -- If 1 minute has passed if Counter = ClockFrequencyHz * 60 -1 then Counter <= 0; State <= StopWest; end if; -- Yellow in west/east direction when StopWest => NorthRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed if Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= NorthNext; end if; end case; end if; end if; end process; end architecture;
A forma de onda após inserirmos o
run 5 min
comando no console ModelSim:Análise
Declaramos um tipo enumerado com todos os oito estados diferentes de nossos semáforos. Em seguida, declaramos um
state
sinal deste novo tipo que criamos. Isso significa que o sinal pode ter apenas um dos oito valores de estado nomeados e nenhum outro valor. O FSM foi implementado usando uma instrução Case dentro de um processo cronometrado. Em cada borda ascendente do relógio, o processo é ativado e o
state
sinal é avaliado. O código dentro de exatamente um dos when
escolhas (ramificações) podem ser executadas, dependendo do estado atual. Em nosso código, é o valor do
Counter
sinal que desencadeia mudanças de estado. Quando o contador atinge um valor predefinido, representando 5 segundos ou 1 minuto, uma nova codificação de estado é atribuída ao State
sinal. Então, quando o processo acorda na próxima borda de subida do relógio após o valor do estado ter sido atualizado, o FSM está em um estado diferente. Observe que não estamos atribuindo
'0'
a qualquer sinal em qualquer um dos when
escolhas. Isso ocorre porque demos a todos os sinais de saída um valor padrão de '0'
no início do processo. Você deve se lembrar de um tutorial anterior que é o último valor atribuído a um sinal que se torna efetivo. As atribuições de sinal tornam-se efetivas somente após o término do processo. Se atribuirmos '0'
ao sinal no início do processo e, em seguida, '1'
em um dos when
escolhas, o sinal terá o valor '1'
. Podemos ver pela forma de onda que o
State
ciclos de sinal através dos oito estados. Os estados verdes constantes duram um minuto, portanto, a imagem da forma de onda foi cortada no North
e West
estados. Retirada
- Algoritmos geralmente são implementados como máquinas de estado finito (FSMs)
- Um FSM pode ser implementado usando uma instrução case em um processo com clock
- Os estados FSM podem ser implementados em um tipo enumerado
Ir para o próximo tutorial »
VHDL
- Máquina de estado finito
- Como garantir o melhor desempenho da máquina de estado Qt
- Como criar uma lista de strings em VHDL
- Como criar um testbench orientado por Tcl para um módulo de bloqueio de código 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 criar um testbench de autoverificação
- Como criar uma lista vinculada em VHDL
- Como a OMNI CNC Laser Machine cria presentes de Natal personalizados?