Máquinas de estados e síntese de circuitos com VHDL

Após alguns dias trabalhando em um projeto com VHDL resolvi escrever alguma coisa a respeito. Afinal, estou meio afastado do Linux embedded (por enquanto !).  Em especial, este post é sobre máquinas de estados e síntese de circuitos com VHDL.

Existem diversas formas, boas ou não, de se definir uma máquina de estado. O livro HDL Chip Design, de Douglas Smith, comenta várias configurações e a síntese obtida após a compilação, sendo uma boa referência para evitar designs ruins. Outra referência que pode impedir erros básicos é o artigo The Ten Commandments of Excellent Design, de Peter Chambers. Ele cita alguns problemas comuns na hora do design que podem gerar circuitos instáveis na presença de ruídos ou em frequências mais altas.

Eu resolvi resumir uma pequena máquina de estado e comentar alguns cuidados. Apesar de existir material de VHDL na internet, a visão crítica do código e da síntese é geralmente omitida. Esta visão geralmente vem com o tempo (e com vários designs com problema).

O código em VHDL está ao final do texto. Basicamente ele contrói um pequeno componente chamado Demo, com duas entradas e duas saídas, além de linhas de clock e reset. O circuito não faz nada interessante, foi feito apenas para demonstração da síntese. Neste design, vários aspectos citados pelo Peter Chambers foram abordados explicitamente:

  • All state machine outputs shall always be registered
  • Thou shall use registers, never latches
  • Thy state machine inputs, including resets, shall be synchronous (all input signals shall be registered)
  • Have no dead states in thy state machines
  • Have no logic with unbroken asynchronous feedback lest the fleas of myriad Test Engineers infest thee
  • All decode logic must be crafted carefully – eschew asynchronicity

O circuito sintetizado está abaixo, com comentários logo a seguir.

Note que o reset utilizado no circuito é sempre síncrono, isto é, o reset externo, denominado de async_reset, passa por um processo antes, chamado de reset_proc. Neste processo, um novo sinal de reset, dependente da subida do clock (logo, um registrador), é criado.

As entradas do circuito chegam sempre em registros e as saídas sempre saem de registros. Isto coloca todo o projeto com um domínio de tempo bastante coerente, evitando instabilidades devido a atrasos ou ruídos. Em alguns designs se recomenda o registro duplo das entradas e saídas, aumentando ainda mais a imunidade. Para sistemas com clocks diferentes, os cuidados precisam ser ainda maiores e uma técnica de toggle é comumente empregada.

A máquina de estado gerada está completamente especificada e foi compreendida corretamente pela ferramenta de síntese, como pode ser notado na figura a seguir. Todos os estados estão listados e existe uma condição de reset.

Existem sugestões de máquinas de estado onde tudo é dependente do clock (rising_edge). No entanto, a divisão das partes combinacionais e sequenciais permite um circuito final, em geral, mais rápido. Uma vez que a parte combinacional tenha gerado um resultado, ele é usado na parte sequencial, atualizando as saídas (ver processos demo_seq_proc e demo_comb_proc).

O código fica um pouco mais complexo, mas é fácil perceber que existe um modelo. No fundo, as entradas, sempre com _wire (a_wire e b_wire), são registradas, no momento da subida do clock, em registros internos com mesmo nome mas terminados com _reg (a_reg e b_reg). Já as saídas combinacionais são sinais normais, sem sufixos (c e d). No entanto, uma vez geradas, elas são salvas em registros internos no momento da subida do clock, também com mesmo nome e terminados com _reg (c_reg e d_reg). Estas duas construções são responsáveis pela síntese dos registros vistos no circuito. Note que a atribuição dos sinais de saída (c_wire e d_wire) está em uma posição independente da subida do clock, para evitar justamente que mais um registrador seja inferido pelo compilador.

Reparando o circuito é possível ver que todos os registros possuem sinais de reset ou preset. Isto foi gerado porque os sinais internos são atualizados na condição de reset. Os que são inicializados com zero estão ligados no reset e, os com um, no preset. Não inicializar um sinal irá gerar um registro ondes as linhas de reset/preset não estarão conectadas.

Já na parte combinacional, é importante que todas os sinais possuam uma atribuição em cada estado da máquina. Sem isso, a síntese irá gerar um latch para manter o sinal entre estados, o que é considerado um design ruim. O técnica comum é colocar o valor default fora do case e mudar este valor dentro do case nos estados relacionados (ver sinais a e b, no processo demo_comb_proc).

A seguir, o código em VHDL. Tudo foi feito no Quartus II, que pode ser baixado gratuitamente.  Sugestões, dúvidas ? Comente !

--
-- Marcelo Barros de Almeida
-- marcelobarrosalmeida@gmail.com
--
LIBRARY ieee;
USE ieee.std_logic_1164.all;
--
--            +----------+
--  a_wire -->|   Demo   |--> c_wire
--  a_wire -->|          |--> d_wire
--            +----------+
--                ^  ^
--  async_reset --|  |-- clk
--
entity demo is
	port
	(
		a_wire : in std_logic;
		b_wire : in std_logic;

		c_wire : out std_logic;
		d_wire : out std_logic;

		async_reset : IN STD_LOGIC;
		clk : IN STD_LOGIC
	);
end demo;

architecture demo_arch of demo is

	--
	-- Máquina de estados usada.
	-- Declare os estados e cubra todos eles no case
	-- para que ela seja corretamente identificada.
	--
	type demo_states is (
			DEMO_STATE_A,
			DEMO_STATE_B,
			DEMO_STATE_C,
			DEMO_STATE_D
		);

	signal curr_demo_state: demo_states;
	signal next_demo_state: demo_states;

	-- Registrar todas as entradas
	signal a_reg: std_logic;
	signal b_reg: std_logic;

	-- Registrar todas as saídas. Note que as variáveis
	-- sem _reg são usadas na parte combinacional.
	signal c_reg, c: std_logic;
	signal d_reg, d: std_logic;

	-- Reset síncrono
	signal reset: std_logic;

begin

	--
	-- Este processo faz o reset ser síncrono,
	-- evitando problemas com possíveis temporizações
	-- do reset externo.
	--
	reset_proc:
	process(async_reset, clk) is
	begin
		if(rising_edge(clk)) then
			if async_reset='0' then
				reset <= '0';
			else
				reset <= '1';
			end if;
		end if;
	end process; 

	--
	-- Processo para atribuição do próximo estado.
	--
	next_state_proc:
	process(reset, clk) is
	begin
		if(reset = '1') then
			curr_demo_state <= DEMO_STATE_A;
		elsif(rising_edge(clk)) then
			curr_demo_state <= next_demo_state;
		end if;
	end process;

	--
	-- Processo responsável por atualizar registros
	-- de entrada e saída, dependente da máquina de
	-- estados e de forma síncrona.
	--
	demo_seq_proc:
	process(reset, clk, c_reg, d_reg) is
	begin
		if(reset = '1') then
			-- Garantindo estados iniciais para todos os registros
			a_reg <= '0';
			b_reg <= '1';
			c_reg <= '0';
			d_reg <= '1';	

			c_wire <= c_reg;
			d_wire <= d_reg;

		-- A dependência do clock indica a geração de registradores.
		elsif (rising_edge(clk)) then
			-- Registrando as entradas
			a_reg <= a_wire;
			b_reg <= b_wire;

			-- Registrando as saídas. O if abaixo poderia ser também
			-- um case usando o sinal curr_demo_state, caso vários
			-- estados precisem ser cobertos.
			c_reg <= c;
			if curr_demo_state = DEMO_STATE_C then
				d_reg <= d;
			else
				d_reg <= '1';
			end if;

		end if;

		-- Atualizando as saídas, sem dependência direta do clock
		-- (acaba acontecendo, já que a saída é registrada).
		-- Outra opção é remover a inicialização de c_wire e d_wire
		-- e colocar as duas próximas linhas fora do processo.
		c_wire <= c_reg;
		d_wire <= d_reg;

	end process; 

	--
	-- Máquina combinacional
	--
	demo_comb_proc:
	process(curr_demo_state, next_demo_state, a_reg, b_reg) is
	begin

		-- Garantindo um valor default para todas as variáveis
		-- e impedindo a geração de latches.
		c <= '0';
		d <= '1';

		-- Cubra todos os estados possíveis e sempre determine
		-- o próximo estado. Tenha sempre um estado de entrada
		-- definido.
		case curr_demo_state is
			when DEMO_STATE_A =>
				c <= a_reg;
				d <= b_reg;
				next_demo_state <= DEMO_STATE_B;

			when DEMO_STATE_B =>
				c <= b_reg;
				d <= a_reg;
				next_demo_state <= DEMO_STATE_C;

			when DEMO_STATE_C =>
				c <= a_reg and b_reg;
				d <= a_reg or b_reg;
				next_demo_state <= DEMO_STATE_D;

			when DEMO_STATE_D =>
				c <= a_reg or b_reg;
				d <= a_reg and b_reg;
				next_demo_state <= DEMO_STATE_A;

			when others =>
				next_demo_state <= DEMO_STATE_A;

		end case;

	end process;

end demo_arch;
About these ads
  1. #1 por acassis em junho 26, 2008 - 7:48 pm

    Baixou o nível em Marcelo?
    É assim que eu gosto, embora não entenda muito de VHDL. Lembro de ter visto no livro do Luigi (PROJETO DE PROTOTIPAÇAO DE SISTEMAS DIGITAIS) um exemplo de uma máquina de estados bem simples, não sei se ela poderia ser aplicada neste caso. Lembro que usando o exemplo que ele dava em VHDL eu consegui implementar uma máquina de estado em C, ficou muito simples, apenas vários CASEs.

    []‘s

  2. #2 por Marcelo Barros de Almeida em junho 27, 2008 - 9:44 pm

    O lance não é ser simples e sim funcionar bem em qualquer frequência, com imunidade a ruídos nos pinos. E aí que a coisa pega, Alan.

  3. #3 por Franz em junho 28, 2008 - 11:29 am

    VHDL… Máquinas de estado… “Férias” da facul (tenho um TCC para entregar).
    Muito bom este exemplo. Estes dias eu estava vendo um projeto de controle de motores DC com o Rodrigo Rossin e brincamos com um simulador de protoboard que gerava o código em VHDL (não me lembro o nome), legal para quem quer começar.

  4. #4 por Igor Barros Barbosa em outubro 15, 2008 - 2:11 am

    Vi que você faz referencia ao livro do Douglas que é Otimo.

    Um livro que gostei é Digital Electronics and Design with VHDL by Volnei Pedroni.

    Volnei Pedroni é Brasileiro e da aula na CEFET-PR.
    Sobre a Maquina de Estados existe exemplos no livro do Volnei.

    (Tem tanto que não mexo com VHDL que não me lembro de muitas coisas mais.)

    Se quiser posso ver se desinterro um PID que fiz no Quartus II.

    Abraços

  5. #5 por jedizone em outubro 15, 2008 - 10:08 am

    Se quiser, mande aí que colocamos o PID no ar ! Seria interessante !

  1. Máquinas de estados em VHDL « Rot-13

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

Você está comentando usando sua conta WordPress.com. Sair / Mudar )

Imagem do Twitter

Você está comentando usando sua conta Twitter. Sair / Mudar )

Foto do Facebook

Você está comentando usando sua conta Facebook. Sair / Mudar )

Conectando a %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.

Junte-se a 499 outros seguidores

%d bloggers like this: