ÍNDICE DE CONTEÚDO
Um contador de segundos é um bom exemplo para explorarmos alguns pontos do desenvolvimento de uma descrição de hardware. O contador aqui desenvolvido está em formato decimal (0000~9999).
O projeto foi desenvolvido com o kit de desenvolvimento Cyclone IV, disponível no mercado nacional, os detalhes deste kit podem ser visto no artigo Placa de FPGA com Cyclone IV.
A figura abaixo traz a representação do RTL (register transfer level) do nível mais alto do projeto (top level), nele podemos notar que o projeto está dividido em seis sub módulos de três componentes diferentes.
Contador
O primeiro módulo tem como função efetuar a contagem do ciclo de clock. No momento que este atingir o valor equivalente em Hz, o contador incrementa os dígitos em uma unidade, representando que um segundo se passou e distribui os dígitos em formato BCD ao próximo módulo. Por exemplo:
valor atual = 5639
saída esperada = 0101 0110 0011 1001
A descrição do hardware pode ser vista abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
library ieee ; use ieee.std_logic_1164.all ; use ieee.numeric_std.all ; entity counter is port ( clock_i : in std_logic; reset_i : in std_logic; -- output four digits digit1_o : out std_logic_vector(3 downto 0); digit2_o : out std_logic_vector(3 downto 0); digit3_o : out std_logic_vector(3 downto 0); digit4_o : out std_logic_vector(3 downto 0) ) ; end counter ; architecture rtl of counter is -- 1 sec clock signals constant value_1HZ_c : natural := 50000000; -- frequenci clock value. signal clk_1s : std_logic; signal cnt_s : integer := 0 ; -- signals to count every digit 0001 up to 9999 signal unit_counter : integer := 0; signal tens_counter : integer := 0; signal hundred_counter : integer := 0; signal thousand_counter : integer := 0; begin -- process for increment 1 sec clock process(clock_i) begin if rising_edge(clock_i) then if cnt_s = value_1HZ_c - 1 then cnt_s <= 0; clk_1s <= '1'; else cnt_s <= cnt_s + 1; clk_1s <= '0'; end if; end if; end process; -- process for increment unit value, -- reset retur all values to 0 process(clk_1s, reset_i) begin if reset_i = '0' then unit_counter <= 0; tens_counter <= 0; hundred_counter <= 0; thousand_counter <= 0; else if rising_edge(clk_1s) then unit_counter <= unit_counter + 1; if unit_counter >= 9 then unit_counter <= 0; tens_counter <= tens_counter + 1; if tens_counter >= 9 then tens_counter <= 0; hundred_counter <= hundred_counter + 1; if hundred_counter >= 9 then hundred_counter <= 0; thousand_counter <= thousand_counter + 1; if thousand_counter >= 9 then thousand_counter <= 0; end if; end if; end if; end if; end if; end if; end process; digit1_o <= std_logic_vector(to_unsigned(unit_counter , digit1_o'length)); digit2_o <= std_logic_vector(to_unsigned(tens_counter , digit2_o'length)); digit3_o <= std_logic_vector(to_unsigned(hundred_counter , digit3_o'length)); digit4_o <= std_logic_vector(to_unsigned(thousand_counter , digit4_o'length)); end architecture ; |
Note que no momento que o signal unit_counter atinge o valor de 10, esta retorna para 0 e incrementa o outro signal tens_counter. O processo se repete para os demais dígitos.
O bloco possui ainda um reset, para retornar ao valor 0.
Conversor BCD para 7- segmentos
Este é um módulo básico, presente em quase todos os cursos de FPGA disponíveis. Consiste de uma entrada em BCD e uma saída em 7 segmentos.
Neste projeto, foram utilizados quatro componentes desse, um para cada dígito.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
library ieee ; use ieee.std_logic_1164.all ; use ieee.numeric_std.all ; entity bcd_seven_seg is port ( clock_i : in std_logic; bcd_i : in std_logic_vector(3 downto 0); seven_o : out std_logic_vector(6 downto 0) ) ; end bcd_seven_seg ; architecture rtl of bcd_seven_seg is begin process (clock_i) begin if rising_edge(clock_i)then case bcd_i is when "0000" => seven_o <= "0000001"; -- "0" when "0001" => seven_o <= "1001111"; -- "1" when "0010" => seven_o <= "0010010"; -- "2" when "0011" => seven_o <= "0000110"; -- "3" when "0100" => seven_o <= "1001100"; -- "4" when "0101" => seven_o <= "0100100"; -- "5" when "0110" => seven_o <= "0100000"; -- "6" when "0111" => seven_o <= "0001111"; -- "7" when "1000" => seven_o <= "0000000"; -- "8" when "1001" => seven_o <= "0000100"; -- "9" when "1010" => seven_o <= "0000010"; -- a when "1011" => seven_o <= "1100000"; -- b when "1100" => seven_o <= "0110001"; -- C when "1101" => seven_o <= "1000010"; -- d when "1110" => seven_o <= "0110000"; -- E when others => seven_o <= "0000001"; -- F end case; end if; end process; end architecture ; |
Mux 4 dígitos
O terceiro componente deste projeto está representado por um multiplexador, responsável por integrar os quatro dígitos ao display, variando o anodo, ativando um a cada ciclo de aproximadamente 330µs.
A cada ciclo a saída, o digit_o e o anode_o variam seus valores em quatro fases, sendo uma para unidade, outra para dezena, mais uma para centena e uma última para milhar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
library ieee ; use ieee.std_logic_1164.all ; use ieee.std_logic_unsigned.all ; entity four_digit_mux is port ( clock_i : in std_logic; reset_i : in std_logic; digit1_i : in std_logic_vector (6 downto 0); digit2_i : in std_logic_vector (6 downto 0); digit3_i : in std_logic_vector (6 downto 0); digit4_i : in std_logic_vector (6 downto 0); segment_o : out std_logic_vector (6 downto 0); anode_o : out std_logic_vector (3 downto 0) ) ; end four_digit_mux ; architecture arch of four_digit_mux is signal period_display : std_logic_vector (17 downto 0); signal led_activating_counter : std_logic_vector (1 downto 0); begin -- process add value in period_display process (clock_i,reset_i) begin if reset_i = '0' then period_display <= (others => '0'); elsif rising_edge(clock_i)then period_display <= period_display + 1; end if; end process; -- chance value each 25M/65536 = 2.6 ms led_activating_counter <= period_display(17 downto 16); process (led_activating_counter) begin case led_activating_counter is when "11" => anode_o <= "0111"; segment_o <= digit1_i; when "10" => anode_o <= "1011"; segment_o <= digit2_i; when "01" => anode_o <= "1101"; segment_o <= digit3_i; when others => anode_o <= "1110"; segment_o <= digit4_i; end case; end process; end architecture; |
Contador de 4 dígitos (top level)
O top level deste projeto não possui nenhuma lógica em sua arquitetura, apenas a instância dos componentes e sua integração através dos signals.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
library ieee ; use ieee.std_logic_1164.all ; use ieee.numeric_std.all ; entity counter_4_digt is port ( clock_i : in std_logic; reset_i : in std_logic; digit_o : out std_logic_vector(6 downto 0); anode_o : out std_logic_vector(3 downto 0) ) ; end counter_4_digt ; architecture rtl of counter_4_digt is signal digit1_bcd , digit2_bcd , digit3_bcd , digit4_bcd : std_logic_vector(3 downto 0); signal digit1_seven, digit2_seven, digit3_seven, digit4_seven : std_logic_vector(6 downto 0); component counter is port ( clock_i : in std_logic; reset_i : in std_logic; digit1_o : out std_logic_vector(3 downto 0); digit2_o : out std_logic_vector(3 downto 0); digit3_o : out std_logic_vector(3 downto 0); digit4_o : out std_logic_vector(3 downto 0) ) ; end component; component bcd_seven_seg is port ( clock_i : in std_logic; bcd_i : in std_logic_vector(3 downto 0); seven_o : out std_logic_vector(6 downto 0) ) ; end component ; component four_digit_mux is port ( clock_i : in std_logic; reset_i : in std_logic; digit1_i : in std_logic_vector (6 downto 0); digit2_i : in std_logic_vector (6 downto 0); digit3_i : in std_logic_vector (6 downto 0); digit4_i : in std_logic_vector (6 downto 0); segment_o : out std_logic_vector (6 downto 0); anode_o : out std_logic_vector (3 downto 0) ) ; end component ; begin counter_1 : counter port map ( clock_i => clock_i, reset_i => reset_i, digit1_o => digit1_bcd, digit2_o => digit2_bcd, digit3_o => digit3_bcd, digit4_o => digit4_bcd ); bcd_1 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit1_bcd, seven_o => digit1_seven); bcd_2 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit2_bcd, seven_o => digit2_seven); bcd_3 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit3_bcd, seven_o => digit3_seven); bcd_4 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit4_bcd, seven_o => digit4_seven); four_digit_mux_1 : four_digit_mux port map( clock_i => clock_i, reset_i => reset_i, digit1_i => digit1_seven, digit2_i => digit2_seven, digit3_i => digit3_seven, digit4_i => digit4_seven, segment_o => digit_o, anode_o => anode_o ); end architecture ; |
Testbench e forma de onda
Abaixo pode ser visto o Test Bench desenvolvido para simular este projeto. O Testbench, traduzido literalmente como bancada de teste, é uma descrição auxiliar (mas indispensável), que permite simular o funcionamento da descrição de hardware.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use std.textio.all; use std.env.finish; entity counter_4_digit_tb is end counter_4_digit_tb; architecture sim of counter_4_digit_tb is constant clk_hz : integer := 100e6; constant clk_period : time := 1 sec / clk_hz; signal clk : std_logic := '1'; signal rst : std_logic := '1'; signal digit_tb : std_logic_vector(6 downto 0); signal anode_tb : std_logic_vector(3 downto 0); begin clk <= not clk after clk_period / 2; DUT : entity work.counter_4_digt(rtl) port map ( clock_i => clk, reset_i => rst, digit_o => digit_tb, anode_o => anode_tb ); SEQUENCER_PROC : process begin wait for clk_period * 2; rst <= '1'; end process; end architecture; |
Podemos notar na forma de onda da simulação o fato de que a partir do 1s um dígito apresenta valor 1 e os demais permanecem como 0.
Conexão com os pinos
Abaixo podemos ver a planilha de conexão com os pinos físicos do kit de desenvolvimento.
Conclusão
Esta proposta bem simples consegue nos fornecer a ideia de como integrar componentes em VHDL. Caso queira ver mais a fundo, os links do projeto estão disponíveis neste link!
Se interessou e não sabe por onde começar? Recomendo a publicação Projetos para Começar com HDL.
Trabalho muito bom. Parabéns!