Prévia do material em texto
Projeto Cronômetro Regressivo Setável Giovani Pivato, Valci Costa de Oliveira Universidade de Caxias do Sul, CARVI, DPEI – Engenharia Elétrica giovanipivato@gmail.com, vcoliveira@rge-rs.com.br Resumo Criar, com o uso do kit de desenvolvimento Spartan 3, um cronômetro regressivo setável que decremente um valor setado (indicações externas definidas pelo usuário), o qual possa fazer a contagem decrementando do valor pré-ajustado de 00.00 mm:ss até 99.59 mm:ss. Para o correto funcionamento foram descritas entidades que interfaceiam o display, programou-se o contador, o divisor de freqüência (para a referência de clock necessária, neste caso o clock local da FPGA é de 50mhz) e conectaram-se os elementos ao display através de uma entidade top, utilizando-se de uma abordagem estrutural. . Palavras-chave: Cronômetro, VHDL, Contadores, Clock,, display. 1. Introdução Este artigo tem o propósito de expor a implementação de um Contador Regressivo Setável, usando-se uma linguagem de hardware (VHDL), através de uma ferramenta computacional (ISE versão 11.1). A execução do trabalho teve inicio com o estudo e introdução na linguagem VHDL, sua sintaxe, forma de estruturação, comandos e suas particularidades por ser distinta de uma programação usual, como por exemplo, C++. Após o estudo da linguagem, verificou-se a forma de utilização da ferramenta computacional ISE versão 11.1, que permite que estruturas escritas na linguagem VHDL sejam sintetizadas e inseridas em FPGA’s. Para implementação foram criadas as entidades, clock2, clock_main, cont2_1, varrenovo, mux_disp, display, Top_crono, cont2, mux 4_1. 2. Revisão bibliográfica A linguagem VHDL possibilita descrever em software o comportamento, funcionalidade de um elemento de hardware ou até de um sistema físico inteiro. Uma entidade (entity) é uma abstração que descreve um sistema, uma placa, um chip, uma função ou, até mesmo, uma porta lógica. Na declaração de uma entidade, descreve-se o conjunto de entradas e saídas. Os ports correspondem a pinos e são tratados como objetos de dados. Pode-se atribuir valores ou obtê-los de ports. Cada entrada ou saída possui um modo (mode) de operação. Os modos possíveis são: In, Out, buffer, inout. Respectivamente, permitem entrada de dados, saída de dados, saída de dados com possibilidade de realimentação e a ultima pode substituir qualquer uma das outras. A arquitetura de uma entidade pode ser descrita de três formas distintas, mas que, em geral, conduzem a uma mesma implementação funcional. As descrições são: comportamentais, por fluxo de dados ou estrutural. As regras básicas para formação de nomes são, o primeiro caractere deve ser uma letra, o último não pode ser underscore, não são permitidos 2 underscores em seqüência e maiúscula / minúscula são equivalentes. Os sinais são de vital importância em virtualmente todos sistemas eletrônicos, podendo transmitir dados internamente ou externamente ao sistema, assumindo assim um papel muito importante em VHDL. Os sinais externos são apresentados na entity e os sinais internos são apresentados na architecture. 3. Desenvolvimento Inicialmente, tivemos que verificar como deveria ser o acionamento dos displays. Para acionar um determinado segmento, é necessário que o FPGA acione o mesmo com um sinal lógico 0, ou GND, fazendo o mesmo com o pino correspondente ao anodo comum do caractere onde se deseja que o segmento seja localizado. O display é do tipo anodo comum, mas como o acionamento dos caracteres é indireto, feito através de transistores tipo PNP, o comum (anodo) da cada caractere é acionado com um nível lógico 0, ou GND, isso deve ser observado. Figura 1 - Display Spartan 3E Utilizando como base a tabela 1, conforme Guia do Usuário do Starter Kit Spartan-3, desenvolveu-se o código fonte para que uma entrada codificada no formato BCD acione os segmentos dos displays de modo que o número binário correspondente àquela entrada seja visualizado decimal. Deve-se ressaltar neste caso a necessidade de efetuar a multiplexação dos displays, visto estes compartilharem do mesmo barramento. Tabela 1 - Codigo BCD Após, criamos uma entidade clock_main, que esta conectada ao clock físico da placa Spartan 3 e funciona como um divisor de freqüência, para que o contador decremente e implemente o número no display a cada segundo e n’ao desordenadamente. Foi usada uma variável que permite a correta divisão de freqüência, a partir da conexão ao cristal de 50 MHz presente na placa. Para a criação da entidade clock2, que tem a função de clock auxiliar, utilizou-se a mesma metodologia. No entanto este tem uma freqüência mais alta, pois o seu objetivo é permitir a multiplexação da exibição nos valores dos displays de 7 segmentos, conseqüência do fato de compartilharem o mesmo barramento. A freqüência do clock2 não poderia ser menor que a sensibilidade do olho humano para a visualização dos displays, caso contrário eles piscariam alternadamente de forma visível. Por tentativa e erro, chegou-se a uma velocidade de clock auxiliar tal que eles piscam e os momentos em que estão desativados são rápidos o suficiente para gerar a sensação visual de que todos os displays estão continuamente ativados. A entidade varrenovo tem o objetivo de, através do monitoramento das entradas (clock, push buttons e switches), permitirem o incremento dos valores que serão carregados no contador regressivo propriamente dito, produzindo saídas do tipo inteiro com range definida de acordo com a necessidade de cada um dos displays, sendo que as saídas: q0(dezena de minuto), q1(unidade de minuto), e q3(unidade de segundo) são de range 0 to 9, e a saída q2(dezena de segundo) é de range 0 to 5, pois um minuto possui apenas 60 segundos, motivo pela qual esta saída é diferente das outras. Cabe salientar que, caso o contador contasse em horas, a saída de dezena de minutos também deveria estar compreendida neste intervalo. Possui também uma entrada “trava”, a qual, em conjunto com a entidade cont2_1, pára o contador e permite o armazenamento dos valores nos registradores, e uma entrada “reset”, como o próprio nome já diz, reseta todos os registradores, gerando a todos o valor ‘0’. O contador regressivo propriamente dito, nesse caso é a entidade cont2_1. Esta entidade tem a função de decrementar o valor recebido nas suas entradas do tipo inteiro até zero. Isto é feito através de uma série de registradores cascateados, que fazem a contagem regressiva até que se atinja o 0. Foi utilizada também um sinal denominado “flag”, que tem por objetivo disparar à saída uma condição que permite a exibição de uma mensagem visual indicando o fim da contagem(“End”), requisito existente no trabalho. Esta entidade possui três saídas do tipo std_logic_vector de quatro posições que correspondem às saídas de cada um dos displays, e posteriormente através do uso das entidades mux4_1 e mux_disp serão multiplexadas para cada um dos displays nos momentos específicos determinados pela condição de operação da máquina de estados. A entidade mux4_1 realiza a multiplexação dos seguimentos de display, setando qual dos quatro displays recebera a informação enviada pela entidade cont2_1. A entidade mux_disp também tem papel fundamental na realização da tarefa de multiplexação, é uma máquina de estados com um contador interno que alterna os valores das saídas, que serão recebidos pela entidade mux4_1 e permitirão a alternância entre os displays. Também controla os momentos em que o ponto será ativado, ou seja, somente quando o display que estiver sendo exibido for o de unidade de minutos. O ponto tem a funçãoexclusiva de separar minutos e segundos. A entidade display tem a função de converter os valores binários na saída da entidade Top_crono para os valores decimais, de acordo com a metodologia descrita no início deste texto e seguindo os modelos disponibilizados. No entanto, foram feitas alterações na entidade, para que fosse viabilizada a exibição da mensagem visual quando do fim da contagem (End). Assim o valor correspondente ao “C”, que é “0110001” foi substituído por “1111111”, o que resulta na exibição de um display totalmente apagado, e o valor correspondente ao “F”, “0111000”, foi substituído por “1101010”, o que resulta na exibição de um “n”. Na entidade Top_crono, são interligadas as entidades supra descritas, através de port map, permitindo que o funcionamento do cronômetro seja como o solicitado pelo usuário e projetado. São utilizados vários sinais internos para o transporte das informações entre uma entidade e outra. A última etapa do projeto foi a criação do arquivo UCF, com a informação onde cada pino de entrada e saída da entidade top deveria estar ligado, permitindo a implementação do projeto e a interação com o usuário, pré-requisito essencial para a realização do projeto. 4. Resultados obtidos A implementação do projeto do Contador Regressivo Setável foi realizado com êxito, apresenta comportamento satisfatório ocupando 145 Slices, ou seja 7 % dos Slices da FPGA, de acordo com relatório do programa. 5. Conclusões Durante o desenvolvimento deste projeto foram enfrentadas dificuldades, sobretudo, pelo desconhecimento das particularidades da linguagem VHDL e da utilização da ferramenta ISE 11.1. Onde mesmo com as entidades sintetizadas, à medida que eram feitas tentativas de gravação na FPGA, a ferramenta acusava erro. A descrição incorreta na entidade de entrada de valores (varrenovo), embora sintetizada corretamente, gerou inúmeros problemas na implementação e etapas posteriores. Isso provou, que, para a criação de Máquinas de Estados Finitos, deve-se ter muita certeza das particularidades da linguagem VHDL, caso contrários pode-se criar máquinas com funcionamento errôneo ou aleatório. O domínio da forma de utilização dos registradores também foi crucial para a criação de uma entidade da entidade que realizasse o decremento dos valores com êxito. Também é importante salientar que a possibilidade de utilização de sinais, entradas e saídas do tipo inteiro também são de bastante valia para a criação de projetos deste tipo, pois evita a necessidade de criação de entidades conversoras binário-decimal. Após ajustes e correções, a implementação funcionou conforme esperado, sendo possível ajustar um valor nos display`s e decrementá-lo. Anexo 1 – Códigos fonte das implementações ENTIDADE clock2 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity clock2 is generic( setpoint: integer := 100000); port( clk_in: in std_logic; rst: in std_logic; clk_out: out std_logic); end clock2; architecture Behavioral of clock2 is signal on_off: std_logic; begin process(clk_in,rst) variable contador: integer range 0 to setpoint; variable error: integer; begin if rst = '1' then on_off <='0'; contador := 0; error := 0; elsif clk_in'event and clk_in = '1' then contador := contador + 1; if contador = (setpoint/2) then contador := 0; on_off <= NOT on_off; else error := 1; end if; else error := 1; end if; end process; clk_out <= on_off; end Behavioral; ENTIDADA display library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity display is port( in_disp: in std_logic_vector (3 downto 0); point_in: in std_logic; point_out : out std_logic; out_disp: out std_logic_vector (6 downto 0)); end display; architecture fin of display is begin out_disp <= "0000001" when in_disp= "0000" else -- 0 "1001111" when in_disp= "0001" else -- 1 "0010010" when in_disp= "0010" else -- 2 "0000110" when in_disp= "0011" else -- 3 "1001100" when in_disp= "0100" else -- 4 "0100100" when in_disp= "0101" else -- 5 "0100000" when in_disp= "0110" else -- 6 "0001111" when in_disp= "0111" else -- 7 "0000000" when in_disp= "1000" else -- 8 "0000100" when in_disp= "1001" else -- 9 "0001000" when in_disp= "1010" else -- a "1100000" when in_disp= "1011" else -- b "1111111" when in_disp= "1100" else -- "1000010" when in_disp= "1101" else -- d "0110000" when in_disp= "1110" else -- e "1101010"; -- n point_out <= point_in; end fin; ENTIDADE mux_disp library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mux_disp is port( clk_in: in std_logic; rst: in std_logic; sel_disp: out std_logic_vector (3 downto 0); sel_mux: out std_logic_vector (1 downto 0); point_out: out std_logic ); end mux_disp; architecture fin1 of mux_disp is begin process(clk_in,rst) variable count: integer range 0 to 4; variable error: integer; begin if rst = '1' then sel_disp <= "0000"; sel_mux <= "00"; point_out <= '1'; elsif rst='0' and clk_in'event and clk_in='1' then if count=1 then sel_disp <= "0111"; sel_mux <= "00"; point_out <= '1'; elsif count=2 then sel_disp <= "1011"; sel_mux <= "01"; point_out <= '0'; elsif count=3 then sel_disp <= "1101"; sel_mux <= "10"; point_out <= '1'; elsif count=4 then sel_disp <= "1110"; sel_mux <= "11"; point_out <= '1'; else error := 1; end if; count := count+1; else error := 1; end if; end process; end fin1; ENTIDADE varrenovo library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity varrenovo is port (rst, clk, trava : in std_logic; val_in : in std_logic_vector (3 downto 0); d0, d1, d3: out integer range 0 to 9; d2 : out integer range 0 to 6); end varrenovo; architecture nap of varrenovo is signal q0, q1, q3: integer range 0 to 9; signal q2 : integer range 0 to 6; begin process( rst, Val_in(0), val_in(1), val_in(2), val_in(3), trava, clk) Begin If rst = '0' then if trava = '1' then if (clk'event and clk='1') then if (val_in (0) ='1') then q0 <= q0 +1; else if (val_in (1) ='1') then q1 <= q1 +1; else if (val_in (2) ='1') then q2 <= q2 +1; else if (val_in (3) ='1') then q3 <= q3 +1; end if; end if; end if; end if; end if; else q0 <= q0; q1 <= q1; q2 <= q2; q3 <= q3; end if; else q0 <= 0; q1 <= 0; q2 <= 0; q3 <= 0; end if; end process; d0 <= q0; d1 <= q1; d2 <= q2; d3 <= q3; end nap; ENTIDADE clock_main library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity clock_main is generic(setpoint: integer := 50000000); port( clk_in: in std_logic; rst: in std_logic; clk_out: out std_logic); end clock_main; architecture fin2 of clock_main is signal on_off: std_logic; begin process(clk_in,rst)variable contador: integer range 0 to setpoint; variable error: integer; begin if rst = '1' then on_off <='0'; contador := 0; error := 0; elsif clk_in'event and clk_in = '1' then contador := contador +1; if contador = (setpoint/2) then contador := 0; on_off <= NOT on_off; else error := 1; end if; else error := 1; end if; end process; clk_out <= on_off; end fin2; ENTIDADE cont2_1 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity cont2_1 is port (rst, clk_in, trava : in std_logic; signal_out0: out std_logic_vector (3 downto 0); signal_out1: out std_logic_vector (3 downto 0); signal_out2: out std_logic_vector (3 downto 0); signal_out3: out std_logic_vector (3 downto 0); d0, d1, d3 : in integer range 0 to 9; d2 : in integer range 0 to 6); end cont2_1; architecture nic of cont2_1 is signal estado, novoestado : integer range 0 to 9; signal carry_temp : std_logic; signal q0, q1, q3 : integer range 0 to 9; signal q2 : integer range 0 to 5; signal sel: std_logic_vector(1 downto 0); signal flag : std_logic ; begin process(clk_in, rst, trava) variable fimm: bit; begin if (trava= '1') then q0 <= d0; q1 <= d1; q2 <= d2; q3 <= d3; fimm := '0'; flag <= '0'; elsif (trava='0') then if clk_in'event and clk_in = '1' then if (q0 /= 0 or q1 /= 0 or q2 /= 0 or q3 /= 0) and (fimm = '0') then if q3 = 0 then q3 <= 9; if q2 = 0 then q2 <= 5; if q1 = 0 then q1 <= 9; if q0 = 0 then q0 <= 9; else q0 <= q0 - 1; end if; else q1 <= q1 - 1; end if; else q2 <= q2 - 1; end if; else q3 <= q3 - 1; end if; else q0 <= 0; q1 <= 0; q2 <= 0; q3 <= 0; fimm := '1'; flag <= '1'; end if; end if; end if; end process; signal_out0 <= "1001" when (q0 = 9) else "1000" when (q0 = 8) else "0111" when (q0 = 7) else "0110" when (q0 = 6) else "0101" when (q0 = 5) else "0100" when (q0 = 4) else "0011" when (q0 = 3) else "0010" when (q0 = 2) else "0001" when (q0 = 1) else "1110" when (flag = '1') else "0000"; signal_out1 <= "1001" when (q1 = 9) else "1000" when (q1 = 8) else "0111" when (q1 = 7) else "0110" when (q1 = 6) else "0101" when (q1 = 5) else "0100" when (q1 = 4) else "0011" when (q1 = 3) else "0010" when (q1 = 2) else "0001" when (q1 = 1) else "1111" when (flag = '1') else "0000" ; signal_out2 <= "0101" when (q2 = 5) else "0100" when (q2 = 4) else "0011" when (q2 = 3) else "0010" when (q2 = 2) else "0001" when (q2 = 1) else "1101" when (flag = '1') else "0000" ; signal_out3 <= "1001" when (q3 = 9) else "1000" when (q3 = 8) else "0111" when (q3 = 7) else "0110" when (q3 = 6) else "0101" when (q3 = 5) else "0100" when (q3 = 4) else "0011" when (q3 = 3) else "0010" when (q3 = 2) else "0001" when (q3 = 1) else "1100" when (flag = '1') else "0000" ; end nic; ENTIDADA mux4_1 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mux4_1 is port( mux_in0: in std_logic_vector (3 downto 0); mux_in1: in std_logic_vector (3 downto 0); mux_in2: in std_logic_vector (3 downto 0); mux_in3: in std_logic_vector (3 downto 0); sel: in std_logic_vector (1 downto 0); mux_out: out std_logic_vector (3 downto 0)); end mux4_1; architecture Behavioral of mux4_1 is begin mux_out <= mux_in0 when sel = "00" else mux_in1 when sel = "01" else mux_in2 when sel = "10" else mux_in3; end Behavioral; ENTIDADA Top_crono library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity Top_crono is port ( Clk, Rst, trava: in std_logic; val_in : in std_logic_vector (3 downto 0); sel_disp: out std_logic_vector (3 downto 0); out_disp: out std_logic_vector (6 downto 0); point_out: out std_logic); end Top_crono; architecture nova of Top_crono is component varrenovo is port (rst, clk, trava : in std_logic; val_in : in std_logic_vector (3 downto 0); d0, d1, d3: out integer range 0 to 9; d2 : out integer range 0 to 6); end component varrenovo; component clock2 is port( clk_in: in std_logic; rst: in std_logic; clk_out: out std_logic); end component clock2; component clock_main is port( clk_in: in std_logic; rst: in std_logic; clk_out: out std_logic); end component clock_main; component cont2 is port (rst, clk_in, trava : in std_logic; carry_out : out std_logic; signal_out: out std_logic_vector (3 downto 0); d0: in integer range 0 to 9; carry_in : in std_logic); end component cont2; component cont3 is port (rst, clk_in, trava : in std_logic; carry_out : out std_logic; signal_out: out std_logic_vector (3 downto 0); d0: in integer range 0 to 6; carry_in : in std_logic); end component cont3; component cont2_1 is port (rst, clk_in, trava : in std_logic; signal_out0: out std_logic_vector (3 downto 0); signal_out1: out std_logic_vector (3 downto 0); signal_out2: out std_logic_vector (3 downto 0); signal_out3: out std_logic_vector (3 downto 0); d0, d1, d3 : in integer range 0 to 9; d2 : in integer range 0 to 6); end component cont2_1; component display is port( in_disp: in std_logic_vector (3 downto 0); point_in: in std_logic; point_out : out std_logic; out_disp: out std_logic_vector (6 downto 0)); end component display; component mux_disp is port( clk_in: in std_logic; rst: in std_logic; sel_disp: out std_logic_vector (3 downto 0); sel_mux: out std_logic_vector (1 downto 0); point_out: out std_logic); end component mux_disp; component mux4_1 is port( mux_in0: in std_logic_vector (3 downto 0); mux_in1: in std_logic_vector (3 downto 0); mux_in2: in std_logic_vector (3 downto 0); mux_in3: in std_logic_vector (3 downto 0); sel: in std_logic_vector (1 downto 0); mux_out: out std_logic_vector (3 downto 0)); end component mux4_1; Signal clk_geral: std_logic; signal clk_sec : std_logic; signal d0 : integer range 0 to 9; signal d1 : integer range 0 to 9; signal d2: integer range 0 to 5; signal d3 : integer range 0 to 9; signal mux_in0: std_logic_vector (3 downto 0); signal mux_in1: std_logic_vector (3 downto 0); signal mux_in2: std_logic_vector (3 downto 0); signal mux_in3: std_logic_vector (3 downto 0); signal to_sel_mux: std_logic_vector (1 downto 0); signal decoder_in: std_logic_vector (3 downto 0); signal point : std_logic; begin clock_principal : clock_main port map ( clk_in => clk, rst => rst, clk_out => clk_geral); clock_secundario : clock2 port map ( clk_in => clk, rst => rst, clk_out => clk_sec); Varredor : varrenovo port map (rst => rst, val_in(0) => val_in(0), val_in(1) => val_in(1), val_in(2) => val_in(2), val_in(3) => val_in(3), clk => clk_geral, trava => trava, d0 => d0, d1 => d1, d2 => d2, d3 => d3);contador: cont2_1 port map ( rst => rst, clk_in => clk_geral, trava => trava, signal_out0 => mux_in0, signal_out1 => mux_in1, signal_out2 => mux_in2, signal_out3 => mux_in3, d0 => d0, d1 => d1, d2 => d2, d3 => d3); mux : mux4_1 port map (mux_in0=>mux_in0, mux_in1=>mux_in1, mux_in2=>mux_in2, mux_in3=>mux_in3, sel=>to_sel_mux, mux_out=>decoder_in); muxdisplay: mux_disp port map (clk_in => clk_sec, rst => rst, sel_mux => to_sel_mux, point_out => point, sel_disp => sel_disp); conversor: display port map( in_disp => decoder_in, point_in => point, point_out => point_out, out_disp => out_disp); end nova; Pinagem #PACE: Start of PACE I/O Pin Assignments NET "sel_disp<0>" LOC = "D14" ; NET "sel_disp<1>" LOC = "G14" ; NET "sel_disp<2>" LOC = "F14" ; NET "sel_disp<3>" LOC = "E13" ; NET "clk" LOC = "T9" ; NET "rst" LOC = "K14" ; NET "out_disp<0>" LOC = "N16" ; NET "out_disp<1>" LOC = "F13" ; NET "out_disp<2>" LOC = "R16" ; NET "out_disp<3>" LOC = "P15" ; NET "out_disp<4>" LOC = "N15" ; NET "out_disp<5>" LOC = "G13" ; NET "out_disp<6>" LOC = "E14" ; NET "out_disp<7>" LOC = "P16" ; NET "trava" LOC = "K13" ; NET "Val_in<0>" LOC = "L14" ; NET "Val_in<1>" LOC = "L13" ; NET "Val_in<2>" LOC = "M14" ; NET "Val_in<3>" LOC = "M13" ; #PACE: Start of PACE Area Constraints #PACE: Start of PACE Prohibit Constraints #PACE: End of Constraints generated by PACE