Escolar Documentos
Profissional Documentos
Cultura Documentos
Apostila Verilog UFCG PDF
Apostila Verilog UFCG PDF
Alexandre Scaico
Campina Grande
Dezembro/2000
Projeto de Sistemas Digitais na Atualidade pág. 2
SUMÁRIO
1. INTRODUÇÃO...........................................................................................................................5
2. A LÓGICA PROGRAMÁVEL...................................................................................................7
2.1 - O que é a Lógica Programável? ...........................................................................................7
2.2 - Memórias PROM .................................................................................................................7
2.3 - Os Dispositivos Lógicos Programáveis (PLD) ....................................................................9
2.4 - Arranjos Lógicos Programáveis.........................................................................................10
2.4.1 - PLA..............................................................................................................................10
2.4.2 - PAL..............................................................................................................................11
2.5 - Arranjos de Portas Programáveis (Programmable Gate Array).........................................11
2.5.1 - CPLD (Complex PLD) ................................................................................................12
2.5.2 - FPGA (Field Programmable Gate Array)....................................................................13
2.6 - Considerações finais ..........................................................................................................14
2.7 - Bibliografia ........................................................................................................................15
3. AS LINGUAGENS DE DESCRIÇÃO DE HARDWARE.......................................................16
3.1 - Evolução dos sistemas digitais...........................................................................................16
3.2 - Surgimento das HDL’s ......................................................................................................16
3.3 - Fluxo Típico em um Projeto ..............................................................................................17
3.4 - Importância das HDL’s......................................................................................................18
3.5 - Tendências em HDL’s .......................................................................................................19
3.6 - Bibliografia ........................................................................................................................19
4. VERILOG HDL ........................................................................................................................20
4.1 - O que é Verilog? ................................................................................................................20
4.2 - Por que usar o Verilog HDL? ............................................................................................20
4.3 - Popularidade do Verilog HDL ...........................................................................................21
4.4 - Aplicações do Verilog e outras linguagens HDL ..............................................................21
4.5 - Limitações do Verilog........................................................................................................21
4.6 - Níveis de abstração ............................................................................................................22
4.7 - Nível comportamental x RTL ............................................................................................22
4.8 - Conceitos principais da linguagem ....................................................................................23
4.9 - Metodologias de projeto ....................................................................................................24
4.10 - Estrutura dos módulos......................................................................................................24
4.11 - Projeto hierárquico...........................................................................................................26
4.12 - Instâncias..........................................................................................................................26
4.13 - Representando hierarquia.................................................................................................27
A
1. INTRODUÇÃO
Mas, devido a complexidade dos sistemas atuais, esse tipo de projeto está se tornando
inviável devido a vários problemas que o projeto a nível de portas lógicas acarreta, tais como:
- Alto número de CI’s contendo os circuitos lógicos (portas lógicas) simples necessários;
- Atraso global do sistema alto devido a contribuição individual dos atrasos de cada porta
lógica individualmente;
- Alto custo do projeto;
- Necessidade de um grande layout físico para acomodar todos os componentes;
- Alto consumo do sistema;
- Possíveis erros de conexão e/ou mau contatos (confiabilidade do sistema);
- Possíveis indisponibilidades dos circuitos necessários no mercado, acarretando em atraso na
finalização dos projetos;
- Necessidade de protótipos para testes, que acarreta em mais gastos.
Junte a isso a evolução tecnológica que vem ocorrendo nos últimos 35 anos. Com a
evolução, ocorreu um aumento na capacidade de processamento dos sistemas, e isso acarretou
uma maior complexidade dos sistemas a serem projetados (e com isso um maior número de
portas lógicas necessárias ao projeto). E também gerou uma maior escala de integração dos CI’s,
que é a VLSI (Integração em Altíssima Escala).
Dentre as novas técnicas que surgiram, a que despontou como a mais promissora é a
“descrição de hardware”. Nesta modalidade de projeto, o projetista, com o auxílio do
computador, descreve o hardware a ser projetado (o seu sistema digital) utilizando uma HDL
(Hardware Description Language – Linguagem de Descrição de Hardware). Uma HDL é muito
parecida com uma linguagem de programação de alto nível, como C ou Pascal. O projeto
utilizando HDL se torna parecido com a programação, uma vez que o projetista inicialmente não
A
se preocupa com a tecnologia que vai ser utilizada na implementação do projeto, e sim com a
funcionalidade lógica do projeto.
Após a descrição ser feita, existem várias ferramentas de simulação para testar a
funcionalidade do projeto antes de sua implementação. E isto é importante porque reduz
drasticamente o tempo de testes, uma vez que não é necessária a construção de protótipos e que,
na ocorrência de um erro ou mudança no projeto, é muito simples se modificar a descrição do
sistema em HDL.
Deve-se ressaltar que existem dispositivos programáveis que só podem ser programados
uma única vez (que são os que o projetista envia a descrição para o fabricante programar), e os
que podem ser reprogramados de acordo com a necessidade (que são os programáveis pelo
projetista).
2. A LÓGICA PROGRAMÁVEL
Essas interconexões podem ser entendidas como fusíveis, que de acordo com a
necessidade do projeto podem ou não ser queimados (desfazendo ou não a conexão entre portas
lógicas). Essa “queima” é realizada pelo projetista, utilizando um software de programação do
dispositivo.
- PLA
- PAL
- Dispositivos Lógicos Programáveis Complexos (CPLD)
- Arranjo de Portas Programáveis em Campo (FPGA)
Temos na figura a seguir o modelo de um PROM. Ela internamente nada mais é que uma
estrutura AND-OR, com a matriz AND fixa e a matriz OR programável. Então, podemos ver a
matriz AND da PROM como um decodificador completo de endereços que pode ser programado
a partir da matriz OR. Ao ser produzida, a PROM vem com todas as conexões internas. Para
programá-la devemos aplicar níveis de tensão apropriados a fim de manter ou não a conexão de
cada entrada de cada porta OR (“queimar” ou não os fusíveis internos). Só que uma vez feita a
programação, ela não pode ser desfeita. Com a evolução, sugiram novos tipos de ROMs que
solucionaram essa limitação das PROMs, que são as EPROMs apagáveis por radiação
ultravioleta, e as PROMs apagáveis eletricamente (EEPROM).
Então, podemos ver a PROM não apenas como uma memória apenas de leitura, mas
também como um circuito combinatório genérico de n entradas e m saídas, cuja função lógica
executada pode ser facilmente programável, como mostra a figura abaixo.
- Quando um sistema possui menos saída que o comprimento da palavra da memória, temos
porções de memória que não são utilizadas totalmente.
PLD
Arranjos Arranjos
Lógicos de Portas
Programáveis Programáveis
Os dispositivos que são arranjos lógicos programáveis possuem uma estrutura interna
semelhante baseadas na estrutura interna AND-OR das PROMs. A estrutura consiste de um
número de entradas ligadas a número de portas AND. As saídas das portas AND são conectadas
as entradas de um número de portas OR, cujas saídas são as saídas do dispositivo. Nestes
dispositivos podemos ter tanto as duas matrizes de portas programáveis, quanto apenas a matriz
de portas AND programáveis. E isso gerou dois tipos de dispositivos: as PLAs e as PALs. Estes
dispositivos, assim como as PROMs, só podem se programados um única vez.
2.4.1 - PLA
A estrutura de uma PLA é muito semelhante a de uma PROM. Só que ela possui uma
menor quantidade de portas AND (não formando um decodificador completo), e possuindo tanto
a matriz de portas AND quanto a matriz de portas OR programáveis. Temos a seguir a estrutura
típica de uma PLA.
2.4.2 - PAL
Com o uso da PLA’s foram verificadas que existiam situações em que a flexibilidade da
matriz OR programável era desnecessária, o que não justificava o uso da PLAs. Além disso, a
existência das duas matrizes (AND e OR) programáveis acarretava uma grande propagação de
atraso entre a entrada e a saída do dispositivo (além de uma maior custo do dispositivo).
Por causa disso foi-se criado um dispositivo baseado no PLA só que mais simples, que é
o PAL. O PAL se assemelha ao PLA, tendo apenas a matriz de portas AND programável (a de
portas OR é fixa). Temos a seguir a estrutura interna simplificada de uma PAL.
Essa estrutura básica da PAL pode ser varia da em quatro alternativas básicas:
- Combinatório;
- Entrada/Saída Programada;
- Saídas com registradores e realimentação;
- Saídas com porta XOR.
O PAL mais simples, o combinatório, tem as saídas iguais as somas de produtos, sem
realimentação (o PAL da figura anterior). Os PALs mais complexos realimentam a saída de volta
para a entrada, permitindo que a saída seja também uma entrada. Outra característica de PALs
mais complexos e a existência de registradores na saída, que fornecem aos PALs a possibilidade
de sintetizar circuitos seqüenciais síncronos. Um tipo de PAL bem complexo é o que dispõe de
circuitos de saída que incorporam a porta XOR. Tais dispositivos são normalmente chamados de
aritméticos porque são sempre necessários quando se quer sintetizar unidades aritméticas.
Inicialmente, assim como as PROMs, estes componentes só eram passíveis de uma única
programação. Mas, com o desenvolvimento das técnicas de fabricação, surgiram as versões
programáveis em campo (que podiam ser reprogramadas de acordo com as necessidades do
projetista), dentre as quais podemos destacar os FPGAs e os CPLDs.
De uma maneira genérica os CPLDs (ou EPLDs – Erasable Programmable Logic Device)
podem ser vistos como dispositivos que agregam em sua estrutura vários PLDs (PLA ou PAL)
interligados por conexões programáveis, como ilustra a figura a seguir.
PLD PLD
Entradas Saídas
PLD PLD
Conexões Programáveis
Os CPLDs são programáveis em campo, e podem ser reprogramados quantas vezes seja
necessário.
Tipicamente, os blocos lógicos de um FPGA são idênticos, e cada bloco pode ser
programado individualmente para realizar um pedaço da lógica do projeto (decompondo a
função lógica geral do projeto em funções menores que serão implementadas pelos blocos
lógicos). A complexidade de um bloco lógico pode variar (não se limita a estrutura típica
mostrada acima) consideravelmente entre diferentes FPGAs, indo desde um bloco de
“granularidade fina” implementando uma ou duas funções lógicas de duas ou quatro variáveis,
até blocos de “granularidade grossa” implementando diversas funções lógicas de quatro a dez
variáveis.
Cada FPGA contém um grande número de blocos lógicos, tipicamente entre 50 e 250, e
algumas vezes mais. Cada bloco lógico é conectado aos outros blocos lógicos e aos pinos de E/S
por interconexões programáveis. Temos a seguir alguns exemplos de arquiteturas de FPGAs no
que diz respeito ao blocos lógicos e as interconexões programáveis.
Fig 09 – Exemplos de arquitetura de FPGAs com respeitos as interconexões entre blocos lógicos
Para o caso da programação, cada dispositivo possui o seu próprio software (de síntese
lógica) que é fornecido pelo fabricante. Resta, então, aprender uma nova técnica de projeto. E,
como também já foi mencionado, a técnica mais promissora nos dias atuais é a “descrição de
hardware”. Pois, além das linguagens de descrição de hardware estarem amplamente difundidas
e serem de fácil aprendizado (já que se parecem com uma linguagem de programação de alto
nível), os softwares de síntese lógica (que programam os dispositivos) aceitam o projeto em
linguagem de descrição de hardware para fazerem a programação dos dispositivos. E, dentre as
linguagens de descrição de hardware, a mais promissora nos dias atuais é o Verilog HDL.
Devemos salientar também que, para o usuário final, FPGA e CPLD podem ser vistas
como o mesmo tipo de estrutura. A única diferença está na sua estrutura interna, mas o seu
resultado final é o mesmo. O fato é que um dos grandes fabricantes, a Xilinx usa o nome FPGA,
enquanto outro grande fabricante, a Altera, usa o nome CPLD. Tanto faz usar uma ou a outra
(embora os fabricantes tentem mostrar que a sua estrutura interna é sempre melhor), que se
obtém o mesmo resultado. Na prática, tanto os FPGAs quanto os CPLDs são conhecidos como
FPGAs.
Existe atualmente uma nova tecnologia que permite usarmos um conceito chamado
“cache lógica” com FPGAs. A cache lógica é um meio mais barato de se implementar lógica
mais eficientemente. As funções ativas da aplicação que está sendo executada pelo FPGA podem
ser reconfiguradas durante a execução, enquanto funções inativas são armazenadas em uma
memória mais barata (um EPROM, por exemplo). Quando uma nova função é necessária, ela é
buscada na memória e escrita sobre as antigas. Ou seja, as funções vão sendo buscadas a medida
que são necessárias como em uma cache de computador. Essa técnica foi desenvolvida pela
A
ATMEL Corporation e, segundo a ATMEL, com isso conseguimos um aumento muito grande da
utilização do FPGA, pois estudos seus mostram que em um FPGA de 10000 com uma função
que o utiliza por completo, apenas 2000 são utilizadas ao mesmo tempo. Então, utilizando essa
tecnologia para fazer a cache das outras 8000 portas podemos implementar essa mesma função
com um FPGA de 2000 portas (bem mais barato).
2.7 - Bibliografia
3. DEWEY, Allen M. “Analysis and Design of Digital Systems with VHDL”. Ed. ITP, Boston,
MA, USA, 1997.
6. “Implement Cache Logic with FPGAs”. Aplication Note, Atmel Corporation, 1999.
A Verilog HDL foi introduzida em 1985 pela Gateway Design System Corporation. Até
maio de 1990, com a formação da Open Verilog International (OVI), a Verilog HDL era uma
linguagem de propriedade da Cadence Design Systems, conglomerado que tinha o controle da
Gateway Design System Corporation.
Atualmente ambos os padrões, Verilog e VHDL são amplamente aceitos pelos projetistas de
sistemas digitais.
Descrições de procedimento
Síntese Lógica
Layout físico
Verificação de layout
Implementação
O diagrama de fluxo acima é tipicamente usado para projetos que usam HDL’s. Em
qualquer projeto, as especificações são definidas primeiro. As especificações descrevem de
A
forma abstrata a funcionalidade, interface, e toda a arquitetura do circuito digital a ser projetado.
Neste ponto o projetista não precisa saber como implementar o circuito. A descrição de
funcionamento é então criada para analisar o projeto em temos da funcionalidade, performance,
submissão aos padrões e outras características de alto-nível. As descrições de funcionamento são
manualmente convertidas para as descrições RTL em uma HDL. O projetista tem que descrever
o fluxo de dados desejado no circuito digital. Deste ponto em diante, o processo é feito com o
auxílio de ferramentas de CAD (Computer-Aided Design).
As HDL’s levam muita vantagem em relação aos projetos baseados apenas em esquemas
tradicionais.
• Os projetos podem ser descritos em um nível muito abstrato pelo uso de HDL’s.
Projetistas podem escrever suas descrições sem escolha prévia de uma tecnologia
específica de fabricação. Ferramentas de síntese lógica podem automaticamente
converter os projetos para qualquer tecnologia de fabricação. Se uma nova tecnologia
surgir, os projetistas não necessitam reprojetar seus circuitos. Eles simplesmente entram
com as descrições RTL e/ou comportamental na ferramenta de síntese lógica (desde que a
ferramenta suporte o nível de descrição usado) e ela então cria uma nova organização ao
nível de porta, usando a nova tecnologia de fabricação. A síntese lógica otimiza o circuito
na área e no tempo de processamento, adequando-o a nova tecnologia.
• Através da descrição em HDL’s, a verificação funcional do projeto pode ser feita
facilmente durante as etapas de projeto. Desde que projetistas trabalham em um nível
A
mais alto de abstração (RTL ou comportamental), eles podem otimizar e modificar suas
descrições até que seja alcançada a funcionalidade desejada. A maior parte dos bugs do
projeto são eliminados nesse ponto. Isso causa uma redução significativa no tempo gasto
no projeto.
• Projetos com HDL’s são análogos a programas de computador. Uma descrição textual
com comentários é uma forma fácil de desenvolver circuitos e retirar seus problemas.
Eles também fornecem uma concisa representação do projeto comparado com os
esquemas elétricos tradicionais. Esses esquemas tradicionais são altamente
incompreensíveis para projetos muito complexos.
HDLs são certamente uma tendência no futuro. Com o rápido aumento da complexidade
dos circuitos digitais e com a sofisticação das ferramentas de CAD, HDLs provavelmente se
tornarão o único método de projeto de grandes sistemas.
3.6 - Bibliografia
01. PALNIKTAR, Salmir. Verilog HDL – A Guide to Digital Design and Synthesis, Sun Soft
Press, California, EUA, 1996.
A
4. VERILOG HDL
O Verilog é uma das duas maiores HDL (Hardware Description Language – Linguagem
de Descrição de Hardware) usadas pelos projetista de hardware na industria e mo meio
acadêmico (a outra é o VHDL). Ela é usada para descrever o comportamento e a estrutura de
uma parte de um hardware (ou ele todo), e é muito parecido com uma linguagem de
programação de alto nível (C, para ser mais preciso).
O Verilog foi desenvolvido entre 1984 e 1985 por Philip Moorby que necessitava de um
meio simples, intuitivo e efetivo de descrever circuitos digitais para modelagem, simulação e
análise. Em 1985 a linguagem se tornou propriedade da Gateway Design System Corporation,
que posteriormente foi adquirida pela Cadence Design Systems Inc., que desenvolveu a
linguagem. Até maio de 1990, quando foi criada a Open Verilog International (OVI), o Verilog
era uma linguagem proprietária da Cadence. Com o OVI a Cadence se sentiu motivada a abrir a
linguagem para domínio público com a expectativa que com isso aumentasse rapidamente a
aceitação da linguagem, e com isso a procura por suas ferramentas de desenvolvimento para
Verilog.
Os sistemas digitais atuais são muito complexos. Em seus níveis de maior detalhamento,
verificamos que eles consistem de milhões de elementos (transistores ou portas lógicas). Então,
para sistemas digitais grandes, o projeto a nível de portas lógicas está morto. Por muitas décadas,
esquemas lógicos serviram como a linguagem para projetos digitais. Porém hoje em dia a
complexidade do hardware cresceu para um degrau no qual os esquemas lógicos não são mais
usuais e mostram apenas uma teia de conexões e não a funcionalidade do projeto. Desde 1970,
engenheiros de computação e engenheiros elétricos tem usado as HDL’s, das quais as
proeminentes são o Verilog e o VHDL. Porém o Verilog é usado pela maioria dos
desenvolvedores, dentre os quais os projetistas de empresas como a Sun Microsistems, Apple
Computer e Motorola.
FPGA (ou um ASIC, ou um PLD), onde o mesmo é testado e o projeto é aperfeiçoado para
depois ser enviado para uma fábrica de CI.
O Verilog tem se tornado uma linguagem de descrição de hardware padrão por oferecer
características muito úteis aos projetistas digitais. Dentre as quais podemos destacar:
• O Verilog é uma HDL de propósito geral que é fácil de aprender e usar. Sua sintaxe é
similar a da linguagem de programação C. Projetistas com experiência de programação
em C vão achar fácil aprender Verilog.
• O Verilog permite que diferentes níveis de abstração sejam misturados em um mesmo
modelo. Então, pode-se definir uma parte do hardware em termos de portas lógicas, outra
em termos comportamentais, etc.
• As ferramentas de síntese lógica mais populares suportam o Verilog. Isto torna o Verilog
a linguagem de escolha dos projetistas.
No caso específico do Verilog, temos que inicialmente (meio dos anos 80) ele era usado
para o projeto de ASIC´s (usando ferramentas de síntese para automaticamente criar e otimizar a
implementação). Atualmente, o Verilog também é usado (como já foi mencionando) nas áreas de
FPGA’s e PLD’s (que são sua aplicação principal).
O Verilog é uma linguagem para projeto de sistemas digitais. Até o presente momento ele
não suporta trabalhar com sistemas e sinais analógicos.
O IEEE 1364 define apenas a linguagem Verilog e sua sintaxe, porém ele não descreve os
diferentes estilos do Verilog e quando usá-los.
Cada ferramenta de síntese suporta uma partição da linguagem Verilog (definida pelo
fabricante da ferramenta) e não toda a linguagem. Com isso a vezes é necessário se adequar
(alterar) o seu código (descrição em Verilog) para se usar determinada ferramenta de síntese.
Como já foi mencionado, uma descrição de um sistema digital em Verilog pode ser feita
em vários níveis de abstração, que são os diferentes estilos de escrita em Verilog. A abstração
define o nível de detalhamento necessário ao projeto, especificado por uma dada descrição
desejada. Esses níveis de abstração são:
1. Nível de Layout: É o nível mais baixo de abstração. O projeto neste nível é implementado em
termos de chaves, nós de armazenamentos, e das interconexões entre eles. Este nível
especifica as informações sobre o layout atual do projeto na pastilha de silício, e pode
também especificar informações detalhadas sobre temporização e possíveis efeitos
analógicos.
2. Nível de portas lógicas: Em seguida ao nível de layout vem o nível de portas lógicas. Aqui o
projeto é descrito como uma rede de portas lógicas e registradores, e das interconexões entre
os mesmos. Projetar neste nível é similar a descrever um projeto em termos de diagrama
lógico de portas lógicas. Já o layout e os efeitos analógicos são ignorados. No entanto, o
projeto contém informações sobre a função, a arquitetura, a tecnologia e a temporização.
4. Nível Comportamental: Este é o nível mais alto de abstração. Aqui descrevemos a função do
projeto sem especificar a arquitetura ou os registradores. O projeto pode ser implementado
em termos do algoritmo do projeto desejado sem detalhes da implementação de hardware.
Projetar neste nível é similar a programar em C.
Vamos ver os casos os casos em que um desses níveis (RTL e comportamental) é mais
adequado que o outro:
• A razão para o RTL ser tão importante é que a maioria das ferramentas de síntese que
existem hoje requer que o código esteja escrito em RTL. E nesse nível o projetista ainda
mantém controle sobre a arquitetura dos registradores do projeto.
• Por outro lado, as ferramentas de síntese comportamental geram automaticamente
arquiteturas de portas lógicas e de registradores direto de uma descrição comportamental
em Verilog. Existem poucas ferramentas desse tipo e elas tendem a ser baseadas em
VHDL.
• Geralmente você precisa descrever seu projeto de FPGA ou PLD em RTL para usar as
ferramentas de síntese disponíveis.
• O nível comportamental é usado para criar estruturas de estímulos, para modelar partes
padrão do projeto, ou para criar especificações simuláveis do seu sistema.
1. Concorrência
Como o Verilog nos permite descrever hardware, ele também tem que ser capaz de descrever
atividades que estão ocorrendo em paralelo (atividades concorrentes).
A
B
S
C
D
Fig. 12 – Concorrência
2. Estrutura
Uma característica muito importante do Verilog é a capacidade de descrever estrutura ou
hierarquia. Descrições de estrutura podem ser misturadas com descrições de comportamento.
Também é possível ter apenas a descrição estrutural do projeto em Verilog ( que são as
netlists do Verilog).
A SUM
B U1 U2
Cin U1 CARRY
3. Declarações de procedimentos
Uma descrição em Verilog pode também conter declarações que executam um após o outro
em seqüência como uma linguagem de software tradicional com C ou Pascal. Isso é
conhecido como declarações de procedimento.
4. Tempo
O Verilog nos permite modelar o conceito de tempo, que claramente é uma parte muito
importante da descrição de um sistema eletrônico.
BS
SB SB SB SB
CB CB CB CB CB CB CB CB
BS
SB SB SB SB
CB CB CB CB CB CB CB CB
Agora vamos começar a ver como estruturamos o código em Verilog para fazer a
representação de um projeto. Um projeto é formado a partir da construção de blocos, e no
Verilog cada bloco é chamado de módulo. O módulo é o equivalente a um símbolo em um
projeto baseado em esquema elétrico.
A primeira coisa que temos que fazer quando desenvolvemos um bloco é dar a ele um
nome único. Em seguida temos que definir as interfaces desse projeto e especificar quais são
A
entradas, saídas ou portas bidirecionais. Nós temos que incluir informações sobre a
funcionalidade que é implementada pelo bloco.
Nós podemos ainda ter a necessidade de incluir blocos de nível inferior no projeto. Para
isso temos que definir quais são esses blocos e como eles são interconectados. A esse processo
dá-se o nome de hierarquia.
BlocoA
Entrada 1 Saída 1
Bloco X
Entrada 2 Saída 2
f Bloco Y
O módulo pode ser um elemento ou uma coleção de blocos de projeto de nível inferior.
Tipicamente, os elementos são agrupados em módulos para prover uma funcionalidade comum
que é usada em várias partes do projeto. Um modulo provê a funcionalidade necessária para o
bloco de nível superior através de sua interface de portas (entradas e saídas), mas esconde a
implementação interna. Isto permite ao projetista modificar o conteúdo de um módulo sem afetar
o resto do projeto.
Um modulo é declarado pela keyword module e seu término com a keyword endmodule.
Todo módulo deve conter um identificador para o módulo (seu nome), o module_name,
acompanhado de uma descrição das entradas e saídas do módulo, o module_terminal_list. Deve
conter também a definição de quem é entrada/saída e a descrição de sua funcionalidade, o
module_internals.
module<module_name>
.
.
.
<module_internals>
.
.
.
endmodule
Bloco A
Acima temos o diagrama que mostra os sub-blocos que formam o bloco A e a sua
hierarquia dentro do bloco.
• Ao dividir o projeto em partes menores se torna mais fácil encontrar e depurar erros, já que o
projetista irá lidar com pedaços menores de código.
• Pedaços da lógica (sub-blocos) podem ser identificados e reusados em outras partes do
projeto apenas instanciando ("chamando") o sub-bloco quando necessário.
• Um projeto em blocos pode ser distribuído entre vários projetistas.
4.12 - Instâncias
Um módulo é um modelo, a partir do qual você pode criar objetos. Quando um módulo é
chamado, o Verilog cria um objeto único a partir do modelo. Cada objeto tem seu próprio nome,
variáveis, parâmetros e interfaces de E/S. O processo de criar um objeto a partir de um módulo
modelo é chamado instanciação, e os objetos são chamados de instâncias. No exemplo abaixo
(contador de 4 bits) o bloco de nível superior cria 4 instâncias do flip-flop T (T_FF) modelo, que
por sua vez instância um D_FF e uma porta inversora. Observe que cada instância deve possuir
um nome único.
Atente para o fato de que no Verilog é ilegal cascatear módulos (um dentro do outro), só
é permitido instanciar.
Contador
q0 q1 q2 q3
de 4 bits
q q q q
clock T_FF T_FF T_FF T_FF
tff 0 tff 0 tff 0 tff 0
reset
d
q
clock D_FF
reset
Fig. 19 – Flip-flop D
output [3,0] q;
input clk, reset;
endmodule
output q;
input clk, reset;
wire d;
endmodule
funcionalidade neste nível. Podemos descrever o módulo de nível inferior em qualquer outro
lugar.
No entanto isso significa que o módulo pode ter dois tipos de nomes: nomes de referência
e nomes de instância. Para não gerar confusão vamos ver um exemplo de um somador completo
feito a partir de dois meio somadores. Isso nós dá dois níveis de hierarquia.
somador completo
Neste caso, o bloco meio somador, com nome de referência HALF_ADD é instanciado
duas vezes. Então precisamos de dois nomes de instância U1 e U2 para distinguí-los.
FULL ADD
A SUM
HALF_ADD HALF_ADD
B U1 U2
Cin
OR CARRY
Nomes de instância
Tipicamente você vai instanciar o nível mais alto do seu projeto em um módulo de
estímulo (que serve para testar a funcionalidade do projeto), que então passará a ser o seu bloco
de nível superior.
Uma vez que um projeto ou parte dele é completado, é uma boa prática testá-lo. A
funcionalidade do bloco projetado pode ser testada aplicando-se estímulos e checando os
resultados. Nós chamamos o bloco que faz isso de módulo de estímulo. É uma boa prática
manter o projeto e módulo de estímulo em arquivos separado e identificar os dois como fazendo
parte de um mesmo projeto maior.
Dois estilos de aplicação de estímulos são possíveis. No primeiro estilo o módulo de estímulo
instancia o módulo do projeto e mando e recebe sinais do mesmo (o bloco de estímulo não
possui nem entradas nem saídas).
módulo módulo
módulo projetado de do
estímulo projeto
(resultados)
Como foi visto o Verilog suporta uma metodologia de projeto hierárquico. Cada instância
de um módulo é definida com um identificador (seu nome de instância). Cada identificador em
particular tem uma posição única na hierarquia do projeto. Um nome hierárquico é uma lista de
identificadores separados por pontos para cada nível de hierarquia. Então, cada identificador
pode ser endereçado de qualquer lugar do projeto simplesmente especificando o nome
hierárquico correspondente desse identificador.
O módulo de nível superior é chamado de módulo raiz porque ele não é instanciado em
nenhum lugar do projeto. Ele é o ponto de partida. Normalmente o módulo de nível superior é
um módulo de estímulo (para testar o projeto).
ESTÍMULO
FULL ADD
L1
A SUM
HALF_ADD HALF_ADD
B U1 U2 L3
Cin
L2 OR CARRY
Para essa simulação, o módulo ESTÍMULO é o módulo de nível superior. Como ele não é
instanciado em parte alguma, ele é o módulo raiz. Os identificadores definidos nesse módulo são
A, B, Cin, SUM e CARRY. Módulo raiz instancia U1 (com o identificadores a, b, L1 e L2) e U2
(com os identificadores (L1, cin, sum L3), que são módulos do tipo HALF_ADD e uma porta
básica OR (com os identificadores L2, L3, cr). Como a porta OR é um bloco básico do Verilog e
só é usado uma vez não é necessário instanciá-la. A partir dai podemos identificar os nomes
hierárquicos, que são:
ESTÍMULO ESTÍMULO.FULL_ADD.HALF_ADD.U1
ESTÍMULO.FULL_ADD ESTÍMULO.FULL_ADD.HALF_ADD.U2
ESTÍMULO.FULL_ADD.A ESTÍMULO.FULL_ADD.HALF_ADD.U1.A
ESTÍMULO.FULL_ADD.B ESTÍMULO.FULL_ADD.HALF_ADD.U1.B
ESTÍMULO.FULL_ADD.Cin ESTÍMULO.FULL_ADD.HALF_ADD.U1.L1
ESTÍMULO.FULL_ADD.SUM ESTÍMULO.FULL_ADD.HALF_ADD.U2.L1
ESTÍMULO.FULL_ADD.CARRY ESTÍMULO.FULL_ADD.OR.CARRY
Vamos agora mostrar 2 exemplos simples de módulos para mostrar de uma forma geral
como uma descrição é feita no Verilog.
HALF ADD
A SUM
XOR
B AND CARRY
A palavra chave module define o começo de cada descrição de módulo e é seguida pela lista
de portas. Após a lista de portas vem a declaração das portas. Cada porta é declarada com
entrada, saída ou entrada/saída. Você pode especificar portas como barramento declarando que a
porta tem um comprimento nas declarações de entrada ou saída.
FULL ADD
I1
A SUM
HALF_ADD HALF_ADD
B U1 U2 I3
Cin
I2 OR CARRY
O nome do módulo e lista de portas são seguidos das declarações locais. Nosso
FULL_ADD contém os sinais I1, I2 e I3 que são locais ao projeto (não são nem entradas nem
saídas). Sinais locais são declarados no seu respectivo módulo, após a declaração das portas.
Em seguida vem a instanciação dos 2 meio somadores (U1 e U2). E, como parte da
instanciação, temos que definir as conexões aos terminais dos meio somadores (na mesma
seqüência definida no módulo HALF_ADD). As conexões aos módulos HALF_ADD são feitas
pela posição: a primeira porta listada no HALF_ADD (porta A) é conectada a primeira porta
listada na instanciação do HALF_ADD no módulo FULL_ADD (porta A), e assim por diante.
Nós ainda temos uma pequena seção de código que descreve a função OR que é requeria
pelo somador completo. E, finalmente declaramos o fim da descrição do módulo. Note que o
conteúdo do meio somador não é definido neste nível (é apenas instanciado).
Para se fazer uma descrição em Verilog é necessário ter conhecimentos das várias partes que
compõem a descrição (como estruturar um módulo, como definir entradas e saídas, as
convenções léxicas usadas, os tipos de dados aceitos pela linguagem, etc.), ou seja, dos conceitos
básicos inerente ao Verilog.
Nos itens a seguir teremos uma visão geral acerca desses conceitos para com isso
proporcionar uma motivação para um posterior estudo mais aprofundado da linguagem. Atente
ao fato de que os conceitos estruturais inerentes ao Verilog que serão apresentados a seguir estão
apenas dando uma visão geral para que você possa começar a dar os primeiros passos em
Verilog, e não para que você saia já programando (o quer requer um estudo bem mais detalhado
e dedicado).
As convenções léxicas básicas usadas pelo Verilog são similares as usadas na linguagem C.
O Verilog possui muitos marcadores. Marcas podem ser comentários, delimitadores, números,
strings, identificadores, e palavras-chaves. O Verilog é uma linguagem case sensitive, isto é, ela
diferencia as letras maiúsculas das minúsculas. E, todas as palavras-chaves devem ser escritas
em minúsculas.
Espaços em branco (\b), tabulações (\t) e novas linhas (\n) compreendem o espaço em
branco. Os espaços em brancos são ignorados pelo Verilog, exceto quando estão separando
marcas. Os espaços em branco não são ignorados em strings.
4.18.2 - Comentários
4.18.3 - Operadores
O Verilog aceita três tipos de operadores: unário, binário e ternário. Operadores unários
precedem o operando. Os operadores binários aparecem entre os operandos. Os operadores
ternários possuem dois operadores separados que separam três operandos.
- Valores X e Z:
O x denota um número desconhecido, e o z denota alta impedância.
Um x ou um z definem 4 bits para um número hexadecimal, 3 bits para um número octal
e 2 bits para um número binário. Se o bit mais significativo é um 0, x ou z, e você não
define os bits restantes, os bits mais significativos restantes são automaticamente
preenchido, respectivamente, com 0, x ou z. Se o bit mais significativo for um 1, o
restante será preenchido com 0.
- Números negativos:
São definidos colocando-se um sinal “-“ antes do <comprimento> do número.
4.18.5 - Strings
Uma string é uma seqüência de caracteres delimitados por aspas. A restrição é que uma
string deve ser de apenas uma linha.
Identificadores são nomes dados a objetos para que os mesmos possam ser referenciados no
projeto. Identificadores podem conter caracteres alfanuméricos, “_” e $, porém não podem
iniciar pelo “&” ou por um número.
O Verilog suporta 4 valores e oito pesos para modelar a funcionalidade de sistemas reais.
Temos abaixo esses 4 valores e a definição dos pesos.
Se tivermos um sinal pull e um sinal weak ao mesmo tempo em um fio, o valor do pull irá se
sobressair sobre o weak (esse pensamento vale para todos os níveis). Se os sinais são de pesos
iguais o resultado é um valor desconhecido.
4.19.2 - Nets
4.19.3 - Registradores
Os registradores são definidos pela palavra-chave reg (reg reset, é um exemplo de definição
de um registrador).
4.19.4 - Vetores
Nets ou registradores podem ser declarados como vetores (de comprimento de múltiplos
bits). Se o número de bits não é especificado o Verilog toma como padrão 1.
Note que o bit da esquerda (não importa a definição do vetor) é o bit mais significativo.
Integer: é um registro de tipos de dados de propósito geral usado para manipular quantidades.
São declarados pela palavra-chave integer.
Real: número reais constantes e registros de tipos de dados real são declarados pela palavra-
chave real. Podem ser especificados em notação decimal ou científica. Ele não deve conter
uma declaração de comprimento. Seu valor padrão é 0.
Time: simulações no Verilog terminam com seus respectivos tempos de simulação. O time é
usado para armazenar tempo de simulação. É declarado pela palavra-chave time.
4.19.6 - Arrays
Arrays são permitidos para reg, integer, time e vetores (não são permitidos para variáveis
reais). É importante não confundir array com vetor. Um vetor é um elemento de n-bits de
comprimento, enquanto um array é composto de n elementos de 1-bit ou n-bits de comprimento.
Temos abaixo alguns exemplos.
4.19.7 - Memórias
As memórias no Verilog são simples arrays de registros (como no segundo item do exemplo
anterior).
4.19.8 - Parâmetros
Usamos a palavra-chave parameter para definir constantes. Os parâmetros não podem ser
usados como variáveis.
4.19.9 - Strings
Strings devem ser armazenadas em variáveis do tipo reg. O comprimento do registrador deve
ser suficiente para acomodar toda a string.
4.20 - Módulos
Nome do módulo
Lista de portas, declarações das portas (se existirem portas)
Parâmetros (opcional)
Funções e tarefas
Declaração endmodule
Sbar
(set) Q
Rbar Qbar
(reset)
Fig. 27 – Latch RS
O latch SR tem S e R como portas de entrada e Q e Qbar como portas de saída. O latch SR e
seu bloco de estímulo podem ser modelados conforme o código abaixo.
module estimulo;
wire q, qbar;
reg set, reset;
SR_latch m1(q, qbar, ~set, ~reset);
initial
begin
$monitor($time “set = %b, reset = %b, q = %b\n”, set, reset, q);
set = 0; reset = 0;
#5 reset = 1;
#5 reset = 0;
# 5 set = 1;
end
endmodule
• Na definição do latch SR, perceba que todos os componentes descritos na figura acima estão
presentes no módulo. Nós não temos declarações de variáveis, declarações de fluxo de
dados, ou blocos comportamentais (always ou initial).
• No entanto, o bloco de estímulo contém declarações nome de módulo, wire, reg, e
declarações de variáveis, instanciação de módulos de nível inferior, bloco de
comportamento (initial) e endmodule, mas não contém lista de portas, declaração de portas,
e declarações de fluxo de dados (assign).
• Então, todas as partes exceto module, nome do módulo, e endmodule são opcionais e podem
ser misturados e casados de acordo com as necessidades do projeto.
4.21 - Portas
As portas provêm a interface pela qual o módulo se comunica com o ambiente. Por exemplo,
os pinos de E/S de um CI são as suas portas. O ambiente interage com o módulo através das
portas. O conteúdo do módulo não é visível para o ambiente. Isto provê uma flexibilidade muito
poderosa ao projetista. O conteúdo do módulo pode ser mudado sem afetar o ambiente desde que
a interface não seja modificada. Portas são também chamadas de terminais.
A definição de um módulo contém uma opcional lista de portas. Se o módulo não troca
sinais com o ambiente, não há portas na lista. Considere um somador completo de 4 bits
instanciado em um módulo de estímulo, conforme a figura abaixo.
estimulo
a somador sum
completo
b (4 bits)
c-in fulladd4 c-out
Note que o módulo estimulo é o bloco de nível superior. O módulo fulladd4 tem como
portas de entrada a, b e cin e como portas de saída sum e carry. Então, fulladd4 se comunica com
meio externo. Já o módulo estimulo não necessita de se comunicar com o ambiente, ele apenas se
comunica com fulladd4, então não necessita de um lista de portas. Temos abaixo um exemplo da
declaração desses módulos e de suas listas de portas.
Todas as portas na lista de portas devem ser declaradas no módulo. As portas podem ser
declaradas como input (porta de entrada), output (porta de saída) ou inout (porta bidirecional).
Veja a seguir a declaração das portas do fulladd4.
A
Note que todas as declarações de portas são implicitamente declaradas como wire no
Verilog. Então, se uma porta deve ser declarada como wire, é suficiente declará-la como output,
input ou inout. As portas input e inout são normalmente declaradas como wire. No entanto, se a
porta output deve armazenar algum valor, ela deve ser declarada como reg. Portas input e inout
não podem ser declaradas como reg porque as variáveis reg armazenam valores e esses tipos de
portas apenas refletem as mudanças dos sinais externos a que são conectadas.
Podemos visualizar uma porta como consistindo de duas unidades, uma interna ao módulo e
outra externa ao mesmo. As unidades internas e externas são conectadas. Existem regras que
governam a conexão de portas quando módulos são instanciados em outros módulos. O Verilog
funcionará de maneira anômala se essas regras forem violadas. Essas regras estão ilustradas na
figura abaixo.
net
net inout
input output
- Input:
Internamente, portas do tipo input devem sempre ser do tipo net. Externamente, elas
podem ser conectadas as variáveis reg ou net.
- Output:
Internamente, portas output podem ser do tipo reg ou net. Externamente, elas devem ser
sempre conectadas com nets.
- Inout:
Internamente devem ser sempre do tipo net. Externamente, inouts devem sempre ser
conectadas as nets.
- Portas desconectadas:
O Verilog permite que deixemos portas desconectadas. Você pode deixar uma porta
desconectada instanciando um módulo como é mostrado abaixo.
Conectar por lista ordenada é o método mais intuitivo para a maioria dos iniciantes. Os
sinais a serem conectados devem aparecer na instanciação do módulo na mesma ordem que
as portas na lista de portas da definição do módulo. Vamos considerar o módulo fulladd4
como exemplo (já explorado anteriormente). Para conectar sinais do módulo estimulo por
uma lista ordenada devemos usar um código como o mostrado abaixo. Note que os sinais
externos SUM, C_OUT, A, B, e C_IN aparecem exatamente na mesma ordem que as portas
sum, c_out, a, b, e c_in na definição do módulo fulladd4.
module estimulo;
reg [3:0] A, B; // Declaração das variáveis de conexão
reg C_IN;
wire [3:0] SUM;
wire C_OUT;
fulladd4 fa_orden(SUM, C_OUT, A, B, C_IN); // Instanciação de fulladd4
...
<estímulos>
...
endmodule
Para grandes projetos onde os módulos têm, por exemplo, 50 portas ou mais, lembrar a
ordem das portas na definição do módulo é impraticável e bastante factível ao erro. O
Verilog, então, também provê a capacibilidade de se conectar sinais externos a portas
usando os nomes hierárquicos das portas. Temos abaixo um exemplo para o fulladd4. Note
que você pode especificar as conexões das portas em qualquer ordem desde que o nome da
porta na definição do módulo corresponda corretamente ao sinal externo.
Note que apenas as portas que serão conectadas a sinais externos devem ser especificadas
na conexão de portas por nome. Por exemplo, se a porta c_out não é conectada a sinal
algum, a instanciação de fulladd4 será de seguinte maneira:
Outra vantagem de se conectar portas por nome é que enquanto o nome da porta não é
mudado, a ordem da portas na lista de portas de um módulo pode ser rearranjada se causar
mudanças nas conexões das portas nos módulos instanciados.
Vamos terminar essa introdução do Verilog com alguns exemplos de estruturas de módulos.
Usaremos como exemplo um multiplexador 4 para 1, descrevendo-o em três níveis de abstração
(portas lógicas, RTL e comportamental) para que você perceba os diferentes estilos de escrita de
módulos.
In0 Y0
NotSel1
Sel1
In1 Y1
NotSel0
Sel0
Out
In2 Y2
In3 Y3
output out;
input In0, In1, In2, In3, Sel0, Sel1;
endmodule
In0 and
lógico
In1
and
lógico
or Out
In2 lógico
and
lógico
In3 and
lógico
not not
lógico lógico
Sel1
Sel0
Fig. 31 – Diagrama de fluxo de dados do MUX 4x1
output Out;
input In0, In1, In2, In3, Sel0, Sel1;
assign Out = (~Sel1 & ~Sel0 & In0) | (~Sel1 & Sel0 & In1) | (Sel1 & ~Sel0 &In2)
| (Sel1 & Sel0 & In3)
endmodule
In1
Out
In2
In3
Sel1 Sel0
output Out;
input In0, In1, In2, In3, Sel0, Sel1;
reg Out;
always @(Sel1 or Sel0 or In0 or In1 or In2 or In3)
begin
A
endmodule
4.23 - Bibliografia
01. PALNIKTAR, Salmir. Verilog HDL – A Guide to Digital Design and Synthesis, Sun Soft
Press, California, EUA, 1996.
02. HYDE, Daniel C. CSCI 320 Computer Architecture Handbook of Verilog HDL. Bucknell
University, Lewisburg, PA, 1997. (http://www.eg.bucknell.edu/~cs320/1995-fall/verilog-manual.html)
08. PELLERIN, David. Introduction to VHDL – for synthesis and simulation, Accolade Desing
Automation Inc. (www.acc_eda.com\h_intro.htm)
5. 1 - Considerações iniciais
Como já foi mencionado, cada ferramenta de síntese lógica suporta apenas uma partição
do Verilog (e não toda a linguagem). A ferramenta lógica que vamos utilizar não é uma exceção,
ela também só trabalha com uma partição do Verilog. E é isto que nós vamos abordar neste
capítulo.
Essa ferramenta permite que a descrição seja feita misturando-se os níveis RTL e
comportamental. Vamos então estudar a partição do Verilog RTL e comportamental suportada
pela ferramenta.
Na verdade, não vamos aqui abordar toda a partição do Verilog e as funcionalidades que
a ferramenta suporta, pois isto tornaria este capítulo muito extenso e tedioso. Vamos aqui
abordar basicamente o que é necessário para se iniciar com projetos usando Verilog.
Vamos estudar quais são os tipos de operadores que o Verilog suporta e quais são as
operações que esses operadores realizam; como definir constantes; a estrutura de controle de
eventos; as estruturas if-else, case e for; e veremos como implementar lógica combinacional,
como também lógica seqüencial.
Os operadores suportados pelo Verilog são dos tipos: aritméticos, lógicos, relacionais, de
igualdade, bitwise, de redução, de deslocamento, de concatenação, e condicional. Muitos desses
operadores são parecidos com os da linguagem C. Cada operador (como nas linguagens de
programação) possui um símbolo que o representa. Temos a seguir uma tabela com a lista
completa dos operadores e seus símbolos, agrupados por tipo.
Bitwise ~ NOT Um
& AND Dois
| OR Dois
^ XOR Dois
^ ~ ou ~ ^ XNOR Dois
Redução & AND redutor Um
~& NAND redutor Um
| OR redutor Um
~| NOR redutor Um
^ XOR redutor Um
^ ~ ou ~ ^ XNOR redutor Um
Deslocamento >> Deslocamento a direita Dois
<< Deslocamento a Dois
esquerda
Concatenação { } Concatenação Qualquer número
Replicação {{ }} Replicação Qualquer número
Condicional ?: Condicional três
// A = 4’b0011, B = 4’b0100, C = 6, D = 4
A * B // Multiplica A por B e resultam em 4’b1100
C / D // Divide C por D, truncando a parte fracionária
A + B // Soma A com B, resultando em 4’b0111
B – A // Subtrai A de B, resultando em 4’b0001
C % D // Resulta no resto da divisão de C por D, que é 2
Observe que se um dos operando conter um bit do tipo x, o resultado da operação será x
(não dá pra precisar o valor se um dos operandos não tem um valor preciso). Observe também
que o resultado da operação módulo tem sempre o mesmo sinal do primeiro operando da
operação (Se C fosse –6 o resultado seria –2; e se C fosse positivo e D negativo o resultado teria
sinal positivo).
Os operadores + e – também são usados como operadores unários. Eles são utilizados
para definir o sinal de um operando (por convenção, os operandos sem sinal são considerados
positivos). E, o Verilog representa internamente os números negativos em complemento de 2.
Uma ressalva aqui é que embora tenhamos explicado os operadores / e %, os mesmos não
são suportados pela ferramenta de síntese lógica da Altera (Max+Plus II).
Os operadores lógicos realizam uma função lógica em dois operados (no caso de NOT é
utilizado apenas um operando) e retornam como resultado um O (falso) ou um 1 (verdadeiro) de
acordo com a função lógica que foi executada. Qualquer operando diferente de zero é
considerado como sendo um 1 lógico. Temos a seguir alguns exemplos.
// A = 3, B = 0
A && B // Realiza um AND lógico entre A (1 lógico) e B (0 lógico) resultando em 0
A || B // OR lógico entre A e B, resultando em 1
!A // Negação lógica de A, resultando em 0
!B // Negação lógica de B, resultando em 1
// A = 4, B = 3, X = 4’ b1010, Y = 4’ b1101
A <= B // Resulta em 0
A > B // Resulta em 1
Y >= X // Resulta em 1
Os Operadores de igualdade === e !==, embora mostrados acima, não são suportados
pelo Max+Plus II.
Esses são os conhecidos operadores da eletrônica digital. Eles realizam a operação bit a
bit em dois operandos (ou em um se for o operador NOT). Eles pegam cada bit de um operando
e realizam a operação com o bit correspondente do outro operando (se os operando forem de
comprimento diferentes, o menor é preenchido por zeros). Temos a seguir alguns exemplos.
// X = 4’b1010, Y = 4’b1101
~X // Negação de X, resultando em 4’b0101
X & Y // And entre X e Y, resultando em 4’b1000
X | Y // Or entre X e Y, resultando em 4’b1111
// X = 4’b1010
&X // Equivalente a 1 & 0 & 1 & 0, resultando em 1’b0
|X // Equivalente a 1 | 0 | 1 | 0, resultando em 1’b1
^X // Equivalente a 1 ^ 0 ^ 1 ^ 0, resultando em 1’b0
// X = 4’b1100
Y = X >> 1; // Y é igual a X deslocado de um bit para a direita. Y = 4’b0110
Y = X << 1; // Y é igual a X deslocado de um bit para a esquerda. Y = 4’b1000
Y = X << 2; // Y é igual a X deslocado de dois bits para a esquerda. Y = 4’b0000
Observe que quando os bits são deslocados, os lugares vazios são preenchidos por zeros.
E, temos que os operadores de deslocamentos só são sintetizáveis pelo Max+Plus II se o
operador da direita (número de bits a ser deslocado) for um valor constante.
Esse operador permite fazer uma concatenação de um mesmo número várias vezes de
forma mais simples que com o operador de concatenação, como é mostrado a seguir.
// A = 1’b1, B = 2’b00
Y = {4{A}} // Resulta em Y = 4’b1111
Y = {4{A}, 2{B}} // Resultam em Y = 8’b11110000
O que temos é que ao executar essa linha de código, primeiro é verificado a expressão
condicional, se ela for verdadeira a variável terá como resultado a primeira expressão após a
interrogação (expressão_verdadeira), e caso contrário o resultado será a segunda expressão após
a interrogação (expressão_falsa). Observe os exemplo abaixo.
Para se definir constantes no Verilog utilizamos o operador `define, como é mostrado nos
exemplos abaixo.
`define A = 3;
`define regA = 1110;
Como podemos ver, no primeiro caso definimos uma constante do tipo int de valor 3,
sempre que A for usado na descrição em Verilog estaremos na realidade usando o inteiro 3
(como na liguagem C). e, no segundo exemplo, definimos uma constante do tipo reg de
comprimento 4 e valor 1110.
if (<condição>)
begin
<linhas de código caso a expressão seja verdadeira>
end
else
begin
<linhas de código caso a expressão seja falsa>
end
Caso só tenhamos uma linha de código a ser executada após o if ou o else, não
necessitamos de usar as keywords begin e end. Caso só desejemos que alguma ação seja
executada apenas no caso da condição ser verdadeira, omitimos o else. E, pode-se aninhar vários
if-else. Abaixo temos alguns exemplos.
if (control == 0) y = x + z;
else if (control == 1) y = x – z;
else if (control == 2) y = x * z;
if(regA = regB)
begin
regC = regA + regC;
regB = regC;
end
else regC = regA;
No primeiro caso, se a variável lock for igual a zero (a ! indica negação) a variável buffer
ficará com o mesmo valor da variável data. Pois quando usamos apenas a variável como
condição em um if (if incompleto) estamos na verdade testando o valor da mesma, se é um zero
lógico ou um lógico, e indicando uma ação a ser tomada.
A estrutura case é usada quando temos uma variável que pode assumir vários valores e
desejamos que uma ação seja efetuada para cada um desses valores. Esse efeito também se
consegue utilizando estruturas if-else aninhadas, só que com case fica mais claro de se visualizar
as relações. Tome como exemplo o último exemplo do item anterior.
case (control)
0 : y = x + z;
1: y = x – z;
2: y = x * z;
endcase
Existem ainda outras estruturas usadas para se implementar laços (for, while e repeat).
Essas estruturas são suportadas pela ferramenta de síntese lógica da Altera, mas não serão
abordadas neste documento. Se necessário se reporte a bibliografia para encontrar explicações
sobre essas estruturas e sua sintaxe.
No Verilog podemos fazer com que a ocorrência de eventos (funções, por exemplo) fique
sujeita a mudanças de variáveis conhecidas cujos valores variam entre 0 e 1. Essas mudanças
podem ser a mudança de estado dessa variável escolhida (mudança de 0 para 1 ou 1 para 0), a
subida de nível (variação de 0 para 1), ou então descida do mesmo (variação de 1 para 0). Esse
controle é conseguido usando o operador @ (que significa mudança de nível). Se usarmos
apenas o @ temos que o evento sob controle é evoluído sempre que a variável de controle mude
de estado; se usarmos em conjunto a palavra reservada posedge, estamos indicando que o evento
só ocorre quando ocorrer uma subida de nível da variável (mudança de 0 para 1); e, se usarmos
negedge, estamos indicando que o evento só ocorre quando ocorrer uma descida de nível. Temos
abaixo alguns exemplos hipotéticos.
always @ (posedge d)
begin // Sempre que d subir para 1, as operações entre o begin e o
X = X + 1; // end são efetuadas
Y = 2 * X;
end
O operador de atribuição contínua atribui valores a variáveis do tipo net (wire). Seu
comportamento é como se fosse criada uma conexão direta entre a variável que está recebendo a
atribuição e a função ou variável que está sendo atribuída. Fisicamente é como pegar um sinal é
ligar diretamente a um ponto por meio de um fio.
Abaixo temos um exemplo do uso desse tipo de construção na criação de uma porta AND
e de conexão entre dois nós do projeto. Observe que as atribuições são executadas em paralelo
(aliás, todas as construções, exceto quando indicadas, no Verilog são executadas paralelamente).
assign c = a & b;
assign d = e;
endmodule
Como podemos observar, temos três entradas e duas saídas nesse módulo. Temos que
uma função AND e executada com as entrada a e b e seu valor é então atribuído continuamente a
saída c (quer dizer que a saída c terá sempre o valor do AND ocorrido entre as entradas a e b).
Temos também que a saída d terá valor igual a entrada e (é como se ligássemos um foi entre
esses dois pontos no circuito).
Uma construção always em conjunto com um controle de eventos pode ser usada para
implementar lógica combinacional se sua construção tem as seguintes propriedades:
- O controle de eventos para essa construção não pode ser sensível à mudanças de nível
do clock do sistema (se existir um clock).
- O controle de eventos para a construção é sensível para todas as possíveis mudanças
na saída dessa construção.
Para exemplificar vamos tomar um módulo simples que conta o número de bits 1 em uma
variável do tipo reg.
module proc(d, q)
input [2:0] d;
output [1:0] q;
integer num_bits;
always @ (d)
begin
integer i;
num_bits = 0;
for (i = 0; i < 3; i = i + 1)
if (d[i] == 1) num_bits = num_bits + 1;
end
assign q = num_bits;
endmodule
Como podemos ver, sempre que a variável d mudar ser valor (always @ (d)) teremos um
laço de comparação para saber quantos dos seus bits são iguais a 1, e esse resultado e
armazenado na variável q.
A lógica seqüencial é aquela em que as saídas em um dado instante de tempo são função
das entradas nesse instante de tempo e nos tempos anteriores (ex: registradores, contadores,
latches, etc). A lógica seqüencial é implementada com construções always.
A ferramenta de síntese lógica da Altera (Max+Plus II) permite que sejam criados
registradores com qualquer combinação dos sinais Clear, Preset, Load e Clock síncrono. Os
registradores são criados utilizando construções always. E, para criarmos registradores temos que
observar duas restrições:
- O sinal de clock só pode ser usado no controle de eventos (não pode ser usado em
nenhuma outra parte da construção always).
- Devemos usar variáveis do tipo reg para implementar os registradores (já que as
mesmas armazenam seu estado anterior).
Temos abaixo três exemplo que servem como base para a construção de registradores.
5.8 - Bibliografia
01. Ajuda on-line da ferramenta de síntese lógica Max+Plus II, da Altera Corp.
02. PALNIKTAR, Salmir. Verilog HDL – A Guide to Digital Design and Synthesis, Sun Soft
Press, California, EUA, 1996.
03. HYDE, Daniel C. CSCI 320 Computer Architecture Handbook of Verilog HDL. Bucknell
University, Lewisburg, PA, 1997. (http://www.eg.bucknell.edu/~cs320/1995-fall/verilog-
manual.html)
6. 1 - Considerações iniciais
Neste último capítulo do texto vamos dar uma visão geral acerca da placa de
desenvolvimento de projetos digitais com auxílio de um FPGA, mostrando sua configuração
física, características, limitações e restrições de uso.
Vamos var também os passos necessários para se utilizar a ferramenta de síntese lógica
Max+Plus II para, a partir de uma descrição de seu projeto digital em Verilog, se programar a
placa e realizar os testes com a mesma.
Então, não necessitamos nos preocupar com a montagem física do projeto, e sim apenas
com a descrição e como a descrição fará uso das interfaces e do próprio FPGA. Temos a seguir a
visão superior e inferior da placa.
Como podemos observar, temos na visão inferior o FPGA, que é o núcleo da placa, pois
todo o resto da placa é composta basicamente da interface ao FPGA (seja para programação ou
para testes) e do circuito de clock.
Temos 67 leds, que são utilizados como portas de saída do FPGA para se observar os
dados de saída. Junto a esses leds vemos vários CIs, que na verdade são drivers de corrente para
os leds, para garantir a corrente necessária para o leds funcionarem, uma vez que a corrente de
saída do FPGA é muito baixa. A numeração dos leds é dada a partir da primeira fileira inferior
de leds, começando pela direita com o led 0 até o 15, na segunda fileira do led 16 até 31, até a
última fileira que termina no led 63. Os três leds mais inferiores são os leds 64, 65 e 66.
Temos também 4 conjuntos de 8 chaves de duas posições, que são utilizadas como
entradas para o FPGA. A numeração dessas chaves é da direita para a esquerda a partir de 0 a 31.
E, existe também uma chave seletora de 10 posições, que é utilizada para se aumentar a
capacidade de entradas e saídas do sistema (associando, por exemplo, que determinado conjunto
de leds mostra a saída de um registrador se a chave seletora estiver na posição 1, e a saída de um
somador se a chave seletora estiver na posição 2).
Existe ainda uma chave bipolar (abaixo da chave seletora) que é utilizada para se gerar o
clock manual. E, para se gerar o clock existe na placa um oscilador que gera um clock de 5kHz
ou 0,5kHz, de acordo com uma seleção na placa (que está associada a um arquivo Verilog de
geração de clock, que será abordado mais adiante).
Para se utilizar a placa, é necessário primeiro programá-la. E para isso é necessário uma
ferramenta de síntese lógica fornecida pelo fabricante do dispositivo escolhido para o projeto. No
nosso caso a ferramenta é o Max+Plus II da Altera. Essa ferramenta além de programar a placa,
permite a depuração de erros na descrição Verilog, a simulação do projeto, entre outras
funcionalidades. Para o nosso caso veremos apenas como compilar e depurar erros na descrição
Verilog e como programar a placa.
O arquivo .acf é muito importantes, pois é nele que definimos qual é o dispositivo que
será utilizado (para a ferramenta poder programá-lo) e quais foram as definições de portas do
FPGA. Quer dizer, ao fazermos uma descrição em Verilog, definimos as interfaces do módulo
que criamos, essas interfaces tem que ser atribuídas as portas físicas do FPGA para que o mesmo
possa “saber” que variável corresponde a cada porta física (pino do CI). Então, o arquivo .acf
tem que ter o mesmo nome da arquivo .v para que na hora da programação da placa as portas de
entrada e saída do FPGA sejam atribuídas as variáveis definidas como interfaces do projeto.
module PC(led, swi, onda, bip, Amem, Dmem, OE, WE, clock);
output [66:0] led;
input [31:1] swi;
input [8:0] onda;
input [1:0] bip;
output [13:0] Amem;
inout [15:0] Dmem;
output OE, WE;
input clock;
`include "peacclk.v"
wire OE = 1 ;
wire WE = 1 ;
wire [15:0] Dmem = 0;
wire [13:0] Amem = 0;
endmodule
Só será trabalhado a parte entre início da função e fim da função, que é onde você
escreverá a sua descrição Verilog. E usará também a parte de atribuição aos leds que vem após o
fim da função para definir as saídas do sistema. Temos a seguir um exemplo simples.
module reg_Out(led, swi, onda, bip, Amem, Dmem, OE, WE, clock);
output [66:0] led;
input [31:1] swi;
input [8:0] onda;
input [1:0] bip;
output [13:0] Amem;
inout [15:0] Dmem;
output OE, WE;
input clock;
`include "peacclk.v"
wire OE = 1 ;
wire WE = 1 ;
wire [15:0] Dmem = 0;
wire [13:0] Amem = 0;
endmodule
Como pode ser observado, foram definidas duas variáveis internas, um que armazena
valor (Out, com 8 bits de comprimento) e outra que não armazena (load). Utilizamos então as
variáveis de interface de entrada sw[15:8] que já foi definida para definir as entradas do sistema.
Temos a função (que é um registrador síncrono simples), e utilizamos as variáveis de interface
de saída led[55:48] para definirmos as saídas do sistema.
A
Observe que temos a inclusão de um arquivo chamado peacclk.v que é o arquivo que gera
o clock. Esse arquivo permite a criação de um clock automático ou manual. Devido ao uso desse
arquivo temos algumas restrições no uso da placa. Não devemos utilizar a chaves de entrada de 0
a 3, pois são as chaves que controlam o clock (deixe as três em nível baixo para trabalhar com o
clock manual). E, não podemos utilizar o led 64, que é o led que mostra o valor do clock no
instante atual.
Resumindo, teremos três arquivos para trabalhar, um com extensão .v, um com extensão
.acf e o peacclk.v. Desses três, o único que trabalharemos é o .v, o qual descreveremos a função
de nosso projeto. Atente para o fato de que o arquivo .v, o nome do módulo dentro do arquivo .v,
o arquivo .acf e a definição do chip dentro do arquivo .acf tem que ter o mesmo nome. Você não
pode mudar isso. E, o arquivo .v só pode ter esse único módulo (que é uma restrição da
ferramenta). E esses três arquivos tem que estar dentro do mesmo diretório.
O arquivo .v pode ser editado em qualquer editor de texto, uma vez que ele é um arquivo
texto com a extensão mudada pra .v. E, o arquivo .v pode ser editado dentro do próprio
Max+Plus II.
Com o arquivo .v aberto, temos que em seguida informar ao aplicativo que esse é o
arquivo que queremos trabalhar. Isso é feito acionando o menu File, em seguida Project,
comando Set Project to Current File, como mostra a figura a seguir.
Para se compilar o projeto você deve acionar o menu File, em seguida Project e
selecionar a opção Save & Compile. A figura abaixo ilustra isto.
Atente para o fato de que uma compilação bem sucedida não garante um projeto correto
em relação a função desejada, e sim garante um projeto correto em relação ao Verilog. Isso
porque você pode usar um operador errado em relação a sua função que não indica nenhum tipo
de erro lógico e sim um erro funcional, quer dizer, uma função será realizada mas não será a
desejada.
Uma vez compilado o projeto, devemos passar para a parte de programação da placa.
Observe que a placa deve estar conectada ao computador para se efetuar a programação. Para
isso devemos primeiro entrar no modo de programação, através do menu Max+plus II, comando
Programmer, como é mostrado na figura a seguir.
Aqui você deve clicar no botão Select Programming File, e com isso é aberta uma janela
na qual você irá selecionar o arquivo com extensão .sof cujo nome é igual a seu arquivo .v. Você
retornará a janela anterior e então deve clicar no botão Add (observe que o único arquivo que
deve estar presente na caixa de texto maior é o que você acabou de adicionar, e caso exista
algum outro, selecione-o e clique em delete). Salve o arquivo clicando em Save Jcf. O nome do
arquivo .jcf deve ser igual ao do .v. e ser salvo de preferência no mesmo diretório. Em seguida
saia dessa janela clicando em OK. Observe que o nome do arquivo .jcf agora aparece na janela
Programmer.
Para um mesmo projeto só é necessário criar o arquivo .jcf uma única vez. Se você voltar
para a parte de edição e compilação, ou se sair do programa e abrí-lo novamente, você precisa
apenas restaurar o arquivo .jcf, através do menu JTAG, comando Restore jcf. E, é uma boa
medida de precaução restaurar o arquivo .jcf logo após a sua criação, para garantir que esse
arquivo está realmente selecionado.
Realizados todos esses passo só resta a programação da placa. Para isso deve-se clicar no
botão Configure, na janela Programmer. Será mostrada a evolução da programação da placa.
Quando acabar é sé clicar em OK e proceder com os teste na placa.
Para se realizar os testes na placa é só entrar com os dados e sinais de controle através das
chaves que você definiu como entrada e dar um pulso de clock com a chave bipolar. E, observar
os dados de saída através dos leds definidos como saídas.
Se você observar que as respostas não estão sendo as esperadas, deve dar uma revisada na
descrição Verilog do projeto, corrigir o erro, compilar, programar a placa e testar novamente.
6.5 – Bibliografia
01. Ajuda on-line da ferramenta de síntese lógica Max+Plus II, da Altera Corp.
02. Página da Placa para Ensino de Arquitetura de Computadores
(http://vulcano.dsc.ufpb.br/elmar/cursos/labarc/peac.hym)