tutorial)*)xilinx)ise - del.ufrj.brmario.filho/tutorial.pdf · 4.)vhdl)básico ’...
Post on 26-Jan-2019
259 Views
Preview:
TRANSCRIPT
Tutorial -‐ Xilinx ISE
Universidade Federal do Rio de JaneiroEscola Politécnica
Departamento de Eletrônica e Computação
Autores: Orientador:Artur Lemos Mário VazIoav LichtensteinThiago Lobo
Índice:1. Introdução 32. Instalação 43. Criando um novo projeto 54. VHDL Básico 85. Simulando um projeto 156. Criando um DCM 237. Implementando um projeto na FPGA 268. Exemplos de Código 27
2
1. Introdução O ISE (Integrated Software Environment) é um software criado pela Xilinx para síntese e análise de modelos HDL. O usuário poderá escrever, compilar, realizar análises temporais (utilizando diferentes estímulos como entrada) e/ou configurar FPGA’s com o modelo criado. Este tutorial visa fornecer ao usuário uma visão geral do software, mostrando, de forma simples, desde a ativação do programa até a compilação, visualização de diagramas RTL e configuração da FPGA Spartan 3AN com códigos VHDL. Desenvolveremos o tutorial baseando-‐nos num módulo divisor de freqüência, e forneceremos outros exemplos de código na última sessão.
3
2. Instalação Para ativar o ISE, deve-‐se baixar os arquivos no link https://www.del.ufrj.br/pastas-‐das-‐disciplinas/eel480 -‐sistemas-‐digitais, garantir que estão na pasta ~/Downloads e executar a seguinte instrução no Terminal: cd ; tar xvf ∼/Downloads/Xilinx.tar ; ./ise.sh Feito isso, o ISE deverá estar ativado e para executá-‐lo, deve-‐se utilizar o seguinte comando, também no Terminal: ise & O que nos leva à tela principal do programa:
4
3. Criando um novo projeto Na tela principal do ISE, devemos começar pelo menu “File > New Project” e a janela “New Project Wizard” será aberta. Campos:“Name” -‐ nome do projeto“Location” -‐ localização onde o projeto será salvo*“Description” -‐ descrição do projeto
* -‐ utilizaremos a pasta /tmp, pois eventualmente, o ISE pode gerar arquivos de projeto muito grandes, os quais excederiam o limite por usuário da rede. Para garantir que os arquivos sejam guardados, devemos utilizar o comando Archive, como mostraremos posteriormente.
5
Feito isso, clicar em “Next”. Agora, devemos escolher outros parâmetros de projeto:“Evaluation Development Board” -‐ Spartan 3AN Starter Kit“Synthesis Tool” -‐ XST“Prefered Language” -‐ VHDL“VHDL Source Analysis Standard” -‐ VHDL-‐93
Após clicar, mais uma vez, em “Next”, uma janela que resume os parâmetros do projeto será exibida. Confira os dados e caso estejam corretos, clique em “Finish”. Agora, temos um projeto (.xise) pronto, basta adicionar códigos-‐fonte VHDL e poderemos trabalhar com os modelos. Para importar um código VHDL já escrito, devemos acessar o menu “Project > Add Copy of Source”, navegar até o arquivo e selecioná-‐lo. Isso copiará o arquivo para o workspace do projeto e permitirá sua edição. Sempre que a edição dos arquivos estiver concluída, devemos utilizar o menu “Project > Archive”, para salvar o projeto na pasta do
6
usuário em formato .zip. Para utilizar esse projeto posteriormente, basta descompactar o .zip gerado dentro da pasta /tmp e abri-‐lo na primeira tela do ISE (“Open Project”).
7
4. VHDL Básico VHDL é uma linguagem de descrição de hardware que foi inicialmente utilizada para documentar componentes digitais e acabou sendo aproveitada para simulações/implementações em FPGA desses mesmos componentes. Um arquivo VHDL deve conter, no mínimo, uma entity e uma architecture. A entity consiste na descrição da interface do módulo como uma caixa preta: especificando apenas as entradas e saídas. A architecture descreve o funcionamento do módulo, propriamente dito. Costumamos separar architectures em duas classes: comportamentais e estruturais. As comportamentais servem para definir como um módulo funcionará e as estruturais servem para definir como um módulo de alta hierarquia interliga diferentes módulos “menores” (o que não deixa de ser seu funcionamento). A utilização de módulos já prontos dentro de uma architecture se assemelha à instanciação de um objeto em uma linguagem Orientada a Objetos: especifica-‐se o tipo do objeto (sua Entity), escolhe-‐se um nome e mapeia-‐se suas entradas e saídas (semelhante ao construtor de classes em Java). Dentro da architecture utilizamos sinais (signals) para intercomunicar módulos. É aplicável um paralelo com as variáveis de um programa em Java. Comentários são feitos utilizando-‐se um “-‐-‐”, por exemplo: -‐-‐ Isso é um comentário e não será utilizado na síntese
8
Segue um exemplo básico: uma porta lógica AND:1. -‐-‐ importando std_logic da biblioteca IEEE2. library IEEE;3. use IEEE.std_logic_1164.all;4. 5. -‐-‐ essa é a entidade (entity)6. entity ANDGATE is7. port ( 8. I1 : in std_logic;9. I2 : in std_logic;10. O : out std_logic);11. end entity ANDGATE;12. 13. -‐-‐ essa é a arquitetura comportamental (architecture)14. architecture behavioral of ANDGATE is15. begin16. O <= I1 and I2;17. end architecture behavioral;
Como podemos ver, a leitura do bloco “entity” permite imaginarmos o seguinte:
Depois, em architecture, o funcionamento do bloco é descrito: a saída O recebe a operação bit-‐a-‐bit AND entre as entradas I1 e I2. A operação AND foi incluída quando importamos a biblioteca IEEE.
I1
9
I1
I2O
ANDGATE
Agora, podemos analisar o divisor de freqüência clk_div:
18. library IEEE;19. use IEEE.STD_LOGIC_1164.ALL;20. use ieee.numeric_std.ALL;21. 22. entity clk_div is23. generic (n : integer := 4);24. port (clk_in : in std_logic; 25. div : out std_logic;26. div2 : out std_logic );27. end clk_div;28. 29. architecture divide of clk_div is30. signal cnt, cnt1 : integer := 0;31. signal div_temp, div_temp2 : std_logic := '0';32. 33. begin34. div <= div_temp;35. div2 <= div_temp2;36. process (clk_in) begin37. if (clk_in'event and clk_in = '1') then38. if cnt >= n then39. div_temp <= not(div_temp);40. cnt <= 1;41. else 42. div_temp <= div_temp;43. cnt <= cnt + 1;44. end if;45. end if;46. if (clk_in'event and clk_in = '0') then47. if cnt1 >= n then48. cnt1 <= 1; 49. div_temp2 <= '1';50. else 51. div_temp2 <= '0';52. cnt1 <= cnt1 + 1;53. end if;54. end if;55. end process;56. end divide;
Como podemos ver, o módulo clk_div é descrito por uma entrada (clk_in) e duas saídas (div1 e div2). O generic n : integer := 4 é equivalente a um #define em C. n será
10
substituído em todos os lugares que aparece no código por 4. Trata-‐se de uma constante. Dentro da architecture, vemos um process, que é como uma função em C. Todo processo leva uma lista de sensibilidade, que no caso é (clk_in). Isso indica que sempre que clk_in for atualizado, o process será executado. Há, também, 4 signals: 2 contadores e 2 temporários que são mapeados às saídas do bloco, como indicam as linhas:
57. div <= div_temp;58. div2 <= div_temp2;
Sabendo-‐se disso, podemos realizar o resto da análise como em qualquer linguagem de programação. Perceberemos que div é simplesmente clk_in dividido por n e que div2 é um clock cujo período é o mesmo de div, porém seu duty-‐cycle (tempo em nível alto) é de 25%, ao contrário dos 50% em clk_in e div.
11
Agora, podemos analisar o outro módulo, base_tempo, que utiliza o módulo clk_div para dividir a frequência do sinal de entrada clk. Vale ressaltar o fato de que um DCM (Digital Clock Manager foi necessário, voltaremos a falar sobre ele na sessão 5):
59. library IEEE;60. use IEEE.STD_LOGIC_1164.ALL;61. use ieee.numeric_std.ALL;62. library UNISIM;63. use UNISIM.Vcomponents.ALL;64. 65. entity base_tempo is66. generic (n: integer := 2);67. port (clk : in std_logic;68. clk_out : out std_logic;69. clk_out_2 : out std_logic;70. locked : out std_logic);71. attribute LOC : string ;72. attribute LOC of clk : signal is "E12";73. attribute LOC of locked : signal is "W21";74. attribute LOC of clk_out : signal is "Y22";75. attribute LOC of clk_out_2 : signal is "Y18";76. end entity;77. 78. 79. architecture estrutural of base_tempo is80. component clk_div81. port (clk_in : in std_logic; 82. div : out std_logic;83. div2 : out std_logic);84. end component;85. 86. COMPONENT dcm187. PORT(CLKIN_IN : IN std_logic;88. CLKFX_OUT : OUT std_logic;89. CLKIN_IBUFG_OUT : OUT std_logic;90. CLK0_OUT : OUT std_logic;91. LOCKED_OUT : OUT std_logic);92. END COMPONENT;93. 94. signal clk_int : std_logic;95. 96.97.98.99.
12
100.begin101.clk_divider : clk_div port map 102. (clk_in => clk_int, 103. div => clk_out, 104. div2 => clk_out_2);105. 106.Inst_dcm1 : dcm1 PORT MAP (107. CLKIN_IN => clk,108. CLKFX_OUT => clk_int,109. CLKIN_IBUFG_OUT => open,110. CLK0_OUT => open,111. LOCKED_OUT => locked);112.end estrutural;
Começaremos a análise pela entity: como esperado, há uma entrada clk e duas saídas clk_out e clk_out_2. Há uma saída extra chamada locked, que simplesmente diz se o DCM está ou não sincronizado. As linhas 113-‐117 associam sinais da entity a componentes da FPGA na qual o código será carregado. Exceto por “E12”, todos os sinais são mapeados a LEDS ou contatos, que podem ser identificados pelos mesmos nomes na placa física:113.attribute LOC : string ;114.attribute LOC of clk : signal is "E12";115.attribute LOC of locked : signal is "W21";116.attribute LOC of clk_out : signal is "Y22";117.attribute LOC of clk_out_2 : signal is "Y18";
A achitecture, como de se esperar, é estrutural, pois base_tempo trata-‐se de um módulo que unifica “pequenos” módulos (clk_div e dcm1). Para instanciarmos módulos, devemos antes fazer uma declaração do tipo “COMPONENT nome ... END COMPONENT;”. Isso foi feito para o clk_div e o dcm1. Feito isso, podemos finalmente mapear as portas dos componentes (tal como inicializar um objeto em Java através de seu construtor) a sinais ou entradas/saídas da entidade.
13
No caso, utilizamos um sinal interno clk_int, que serve como saída do dcm1. Não podemos utilizar diretamente o clock da placa (50mHz), pois há problemas como capacitâncias parasitas que prejudicariam o funcionamento do circuito. De fato, resolvemos esse problema através do dcm1, que coloca uma frequência menor em clk_int para que essa seja utilizada como entrada para o clk_div. As saídas do clk_div são intuitivamente mapeadas às saídas clk_out e clk_out_2 da base_tempo. Acreditamos que a esse ponto, o leitor já adquiriu certa noção de código VHDL e de seu funcionamento, sendo capaz de continuar por si só utilizando referências online para funções mais complexas.
14
5. Simulando um projeto Completados os códigos VHDL, podemos realizar simulações e visualizar seu diagrama RTL através do ISE. Vale lembrar que podemos ver erros de sintaxe em tempo real na aba “Errors”, basta um “CTRL + s” para salvar o código e a janela será atualizada com os erros encontrados:
Para sintetizar os arquivos, devemos selecionar a “Implementation View” em “Design > Hierarchy”. Feito isso, devemos clicar uma vez sobre o nome do componente que será sintetizado:
15
Feito isso, basta um duplo-‐clique em “Synthesize -‐ XST”. Espere alguns instantes e verifique o log de erros para saber se a sintetização foi feita com sucesso. Para visualizar o esquemático RTL, basta expandir “Synthesize -‐ XST”, um dupo-‐clique em “View RTL Schematic” e selecionar “Start with a schematic of the top-‐level block” no diálogo que será aberto.
Com o “Top-‐level schematic” aberto, podemos dar duplos-‐cliques de forma a expandir as “caixas-‐pretas” que compoem o circuito. Podemos, finalmente, simular o código. Selecione a “Simulation View” em “Design > Hierarchy” e clique uma vez sobre o nome do componente que será simulado. Feito isso, expanda “iSIM simulator” e dê duplos-‐cliques, na sequência, em: “Behavioral Check Sintax” (um último teste de sintaxe) e
16
“Simulate Behavioral Model” (a simulação propriamente dita).
Caso tudo corra bem, a janela do simulador (iSim) será aberta:
17
Agora, podemos forçar um valor no clock de forma a observar a saída do circuito (que se trata de um clk_div). Devemos clicar com o botão direito na variável desejada e selecionar “Force clock”. Isso abrirá uma janela com diversas caixas a serem preenchidas:
“Value Radix” -‐ Tipo de clock: binário, ASCII, Decimal etc“Leading Edge Value” -‐ Valor inicial de clock“Trailing Edge Value” -‐ Valor final de clock“Starting at Time Offset” -‐ Quando o clock começa“Cancel After Time Offset” -‐ Quando o clock termina“Duty Cycle (%)” -‐ Razão entre Leading Edge e Trailing Edge“Period” -‐ Auto-‐explicativo
Preenchendo da seguinte forma, obtemos um período de
50mHz.
18
Agora, devemos programar por quanto tempo a simulação será executada. Basta preencher a caixa de texto na barra de ícones e então clicar na setá para a esquerda (apagando a última simulação). Feito isso, devemos clicar no botão de play com uma ampulheta e na terceira lupa (com um X) para ajustar a escala dos gráficos ao tamanho da janela.
Esse é o resultado final. Como podemos ver, o clock foi devidamente dividido.
19
Outra forma de impor valores de clock e de certas entradas é utilizar um TestBench. Para tanto, devemos selecionar a “Implementation View” em “Design > Hierarchy”, clicar com o botão direito sobre o módulo principal, ir em “New > Source > TestBench”. Devemos dar um nome ao TestBench (normalmente adicionando-‐se o sufixo _tb ao nome do componente original) e ao confirmar seremos levado à janela de seu código fonte (o código exemplo foi usado em um debouncer, que será descrito na última sessão):
118.LIBRARY ieee;119.USE ieee.std_logic_1164.ALL;120. 121.ENTITY debouncer2_tb IS122.END debouncer2_tb;123. 124.ARCHITECTURE behavior OF debouncer2_tb IS 125. 126. COMPONENT debouncer2127. PORT(128. Clk : IN std_logic;129. Key : IN std_logic;130. pulse : OUT std_logic131. );132. END COMPONENT;133. 134. 135. -‐-‐Inputs136. signal Clk : std_logic := '0';137. signal Key : std_logic := '0';138. 139. -‐-‐Outputs140. signal pulse : std_logic;141. 142. -‐-‐ Clock period definitions143. constant Clk_period : time := 10 ms;144. 145.BEGIN146. 147. -‐-‐ Instantiate the Unit Under Test (UUT)148. uut: debouncer2 PORT MAP (149. Clk => Clk,
20
150. Key => Key,151. pulse => pulse152. );153. 154. -‐-‐ Clock process definitions155. Clk_process :process156. begin157. Clk <= '0';158. wait for Clk_period/2;159. Clk <= '1';160. wait for Clk_period/2;161. end process;162. 163. 164. -‐-‐ Stimulus process165. stim_proc: process166. begin 167. Key <= '0';168. wait for Clk_period;169. Key <= '1';170. wait for Clk_period * 6;171. Key <= '0';172. wait for Clk_period * 4;173. Key <= '1';174. wait for Clk_period * 4;175. Key <= '0';176. wait;177. end process;178. 179.END;
Como pode-‐se ver, o módulo do exemplo possui duas entradas clk e key e uma saída pulse. Podemos inicializar as variáveis como nas linhas 136 e 137 e também definir um período para o clock. Feito isso, podemos implementar o Clk_process, que define como o clock mudará no tempo. No caso, como podemos ver, ele é forçado a 0, meio período é esperado, ele é forçado a 1, meio período é esperado e o processo se repete, gerando uma onda quadrada. O mesmo pode ser feito para os estímulos (as entradas) do componente no processo stim_proc. Podemos usar a
21
palavra reservada wait para definir os atrasos entre as mudanças de valores (que também podem ser definidas). Com o TestBench feito, basta simulá-‐lo no lugar do componente original e teremos o processo automatizado, sem necessidade de forçar clocks ou coisas do tipo.
22
6. Criando um DCM A criação de um DCM consiste nos seguintes passos: -‐ Selecionar o “Implementation View” em “Design > Hierarchy”-‐ Clicar com o botão direito no projeto e selecionar New Source-‐ Busque por DCM na barra de pesquisa e selecione Single DCM_SP conforme a seguinte imagem:
-‐Dê um nome diferente de dcm, tal como dcm1 ou dcm2, e clique em Finish-‐ Feito isso, você será levado à janela Xilinx Clocking Window-‐Nessa janela é possível selecionar os mais variados parâmetros para o clock que o DCM transformará.
23
Selecione conforme as exigências de seu projeto (como multiplicar/dividir o clock)
-‐ Clique em Next, selecione “Use Global Buffers for all Selected Clock Outputs”-‐ Clique em Next, selecione “Show all Modifiable Outputs” e clique em Finish
24
-‐O DCM está criado, agora basta utilizar o código para o componente de DCM como mostrado na sessão 4.
25
7. Implementando um projeto na FPGA Caso os sinais da entity do componente já estejam mapeados para partes da FPGA, como mostrado na sessão 4, você está pronto para implementar o código na placa. É importante, obviamente, que a placa esteja ligada ao PC pela interface USB e esteja devidamente alimentada. Em primeiro lugar, devemos escolher a “Implementation View” em “Design > Hierarchy”, feito isso, clique uma vez sobre o módulo principal do seu projeto e clique duas vezes sobre “Configure Target Device”. A janela do iMPACT será aberta. Clique duas vezes em “Boundary Scan” e clique com o botão direito no espaço em branco e selecione “Initialize Chain”. Na primeira miniatura da FPGA, carregue o arquivo .bit que correponde ao seu projeto. Deixei a segunda em “bypass”. Clique com o botão direito sobre a primeira miniatura e clique em “Program FPGA Only”. O programa deve estar carregado na placa. Observe as partes para as quais os sinais foram mapeados.
26
8. Exemplos de códigoDebouncer:
180.LIBRARY ieee;181.USE ieee.STD_LOGIC_1164.all;182.USE ieee.STD_LOGIC_UNSIGNED.all;183. 184.ENTITY debouncer2 IS185. PORT (Clk : IN STD_LOGIC;186. Key : IN STD_LOGIC;187. pulse : OUT STD_LOGIC);188.END debouncer2;189. 190.ARCHITECTURE shift_register OF debouncer2 IS191.SIGNAL SHIFT_KEY : STD_LOGIC_VECTOR(3 DOWNTO 0) := "1111";192. 193.BEGIN194.PROCESS195. BEGIN196. 197. WAIT UNTIL Clk'EVENT AND Clk = '1';198. 199. SHIFT_KEY(2 DOWNTO 0) <= SHIFT_KEY(3 DOWNTO 1);200. SHIFT_KEY(3) <= NOT Key;201. 202. IF SHIFT_KEY(3 DOWNTO 0)="0000" THEN PULSE <= '1';203. ELSE PULSE <= '0';204. END IF;205. 206.END PROCESS;207.END shift_register;
Primeiramente, explicaremos a motivação do circuito: remover trepidações transientes provenientes de chaves mecânicas. O módulo possui duas entradas Clk e Key e uma saída Pulse. Key é o sinal proveniente da chave mecânica, pulse é a saída que corresponde ao sinal da chave “corrigido”. O circuito funciona inicializando o registrador para “1111” e esperando que ele se torne “0000”, para fazer pulse = 1.
27
A forma de atualizar o valor do registrador é ir colocando not key no lado direito do vetor de bits do registrador. Se por 4 clocks seguidos, o valor de key amostrado estiver em 1, o registrador estará no estado “0000” e pulse valerá 1, caso contrário pulse valerá 0. Isso faz sentido pois evita que a saída seja atualizada durante o transiente da vibração da chave, fazendo com que ela só seja ativada algum tempo depois (o suficiente para que a trepidação termine). Logo, haverá um atraso muito pequeno (porém detectável em osciloscópio) na resposta da chave.
A seguinte imagem, adquirida em um osciloscópio ligado à FPGA com o programa do Debouncer carregado demonstra tal atraso e comprova o funcionamento do circuito:
28
A curva mais baixa é key e a mais alta é not pulse, vemos, portanto, que pulse só vai a 1 algum tempo depois de key ter ido a 1. Esse é o tempo do transiente de vibração.
Referências:An ISE Tutorial -‐ Luiz RennóProjeto Debouncer -‐ Camila Simões da Costa Cunha Vasconcellos, Henrique Duarte Faria, Patrick Svaiter, Paulo Roberto Yamasaki CatundaRelatório de Sistemas Digitais: Projeto Base de Tempo -‐ Michael D. Barreto e Silva, Luiz Carlos, Gabriela Dantas
29
top related