Você está na página 1de 81

PROJETO E SIMULAÇÃO DE UM

MICROCONTROLADOR COMO EXEMPLO DE


IMPLEMENTAÇÃO DE UM SISTEMA DIGITAL

Eduardo Frimer

Projeto de Graduação apresentado ao Curso de


Engenharia Eletrônica e de Computação da Escola
Politécnica, Universidade Federal do Rio de
Janeiro, como parte dos requisitos necessários à
obtenção do título de Engenheiro.

Orientador: José Paulo Brafman

Rio de Janeiro

Agosto de 2015
PROJETO E SIMULAÇÃO DE UM
MICROCONTROLADOR COMO EXEMPLO DE
IMPLEMENTAÇÃO DE UM SISTEMA DIGITAL

Eduardo Frimer

PROJETO DE GRADUAÇÃO SUBMETIDO AO CORPO DOCENTE DO CURSO


DE ENGENHARIA ELETRÔNICA E DE COMPUTAÇÃO DA ESCOLA
POLITÉCNICA DA UNIVERSIDADE FEDERAL DO RIO DE JANEIRO COMO
PARTE DOS REQUISITOS NECESSÁRIOS PARA A OBTENÇÃO DO GRAU DE
ENGENHEIRO ELETRÔNICO E DE COMPUTAÇÃO

Autor:
_________________________________________________
Eduardo Frimer

Orientador:
_________________________________________________
Prof. José Paulo Brafman, M.Sc.

Examinador:
_________________________________________________
Prof. Aloysio de Castro Pinto Pedrosa, Ph.D.

Examinador:
_________________________________________________
Prof. Joarez Bastos Monteiro, D. Sc.

Rio de Janeiro – RJ, Brasil

Agosto de 2015

ii
UNIVERSIDADE FEDERAL DO RIO DE JANEIRO
Escola Politécnica – Departamento de Eletrônica e de Computação
Centro de Tecnologia, bloco H, sala H-212, Cidade Universitária.
Rio de Janeiro – RJ CEP 21949-900

Este exemplar é de propriedade da Universidade Federal do Rio de Janeiro, que


poderá incluí-lo em base de dados, armazenar em computador, microfilmar ou adotar
qualquer forma de arquivamento.
É permitida a menção, reprodução parcial ou integral e a transmissão entre
bibliotecas deste trabalho, sem modificação de seu texto, em qualquer meio que esteja
ou venha a ser fixado, para pesquisa acadêmica, comentários e citações, desde que sem
finalidade comercial e que seja feita a referência bibliográfica completa.
Os conceitos expressos neste trabalho são de responsabilidade do(s) autor(es).

iii
AGRADECIMENTO

A Deus por ter me criado e ter dado saúde e força para superar todas as
dificuldades.
Aos meus pais, minha irmã e minha noiva, pelo amor, incentivo e apoio
incondicional.
A esta universidade, seu corpo docente, direção e administração pelo ambiente
criativo е pela oportunidade de cursar Engenharia Eletrônica e de Computação.
Ao meu orientador José Paulo Brafman, pelo suporte no pouco tempo que lhe
coube, pelas suas correções e incentivos.
E a todos que direta ou indiretamente fizeram parte da minha formação.

iv
RESUMO

Este trabalho final de graduação descreve a modelagem, o projeto e a


implementação de um sistema digital para FPGA baseado na linha de
microcontroladores PIC, mais especificamente no PIC16F84.
Este trabalho faz uma descrição das diversas tecnologias estudadas para alcançar
os objetivos propostos. Ele também descreve a metodologia usada e todo o processo de
desenvolvimento desde a modelagem e o projeto até a implementação do sistema
proposto.
O código desenvolvido é versátil e genérico, considerando-se uma
implementação para FPGA, e pode ser usado em diversas aplicações.

Palavras-Chave: PIC16F84, modelagem, implementação, FPGA, sistema digital.

v
ABSTRACT

This bachelor thesis describes the modeling, design and implementation of a


digital system for FPGA based on the PIC microcontroller series, specifically in
PIC16F84.
This study describes various technologies studied to achieve the proposed
objectives. It also describes the methodology used and the entire development process
from modeling and design to implementation of the proposed system.
The developed code is flexible and generic, considering an implementation for
FPGA, and can be used in various applications.

Keywords: PIC16F84, modeling, implementation, FPGA, digital system.

vi
SIGLAS

A/D – Analógico/Digital
ALU – Unidade Aritmética Lógica
CI – Circuito Integrado
CISC – Complex Instruction Set Computer
CLB – Bloco de Lógica Configurável (Configurable Logic Block)
CMOS – Complementary Metal Oxide Semiconductor
CPU – Unidade Central de Processamento
E / S – Entrada/Saída
EEPROM – Electrically-Erasable Programmable Read-Only Memory
FPGA – Field-Programmable Gate Array
GPR – General Purpose Registers
HDL – Linguagem de Descrição de Hardware (Hardware Description Language).
MOSFET –Metal Oxide Semiconductor Field Effect Transistor
PC – Program Counter
PIC – Controlador de Interface Programável (Programmable Interface Controller)
POR – Power-on Reset
PWM – Pulse-Width Modulation
RAM – Random Access Memory
RISC – Reduced Instruction Set Computer
ROM – Read Only Memory
RTL – Nível de Transferência de Registrador (Register Transfer Level)
SFR – Special Function Registers
UART – Universal Asynchronous Receiver/Transmitter
UFRJ – Universidade Federal do Rio de Janeiro
USB – Universal Serial Bus

vii
Sumário
1 Introdução 1

1.1 - Tema ........................................... 1

1.2 - Delimitação ...................................... 2

1.3 - Justificativa ...................................... 2

1.4 - Objetivos ........................................ 3

1.5 - Metodologia ..................................... 3

1.6 - Descrição ....................................... 3

2 Revisão Tecnica 5

2.1 - FPGAs ......................................... 5

2.1.1 - A estrutura de uma FPGA ......................... 5

2.1.2 - Núcleo da FPGA . .............................. 7

2.1.3 - Desenvolvimento de projetos em FPGA ............. 7

2.2 - Microcontroladores ............................... 10

3 Revisão Bibliográfica 14

14
3.1 - RISCuva1 .......................................

3.2 - Picoblaze ....................................... 14

3.3 - Núcleos de Código Aberto ......................... 14

3.4 - Conclusão da Revisão Bibliográfica .................. 15

viii
4 Modelagem do Sistema 17

17
4.1 - Geral ...........................................

4.1.1 - Timer 0 ....................................... 19

4.1.2 - Timer Watch Dog ............................... 21

4.1.3 - Pre-Escalador .................................. 22

4.1.4 - Interrupções .................................... 24

4.1.5 - Portas ......................................... 25

4.1.6 - Condições de RESET ............................ 26

4.2 - CPU ........................................... 27

4.2.1 - Sub-Sistema de Controle .......................... 28

4.2.2 - Sub-Sistema de Dados ........................... 29

4.3 - Memórias ....................................... 30

4.3.1 - Arquivo de Registradores (RAM ) . . . . . . . . . . . . . . . . . . . 30

4.3.2 - EEPROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

4.3.3 - FLASH ....................................... 36

5 Implementação e Resultados 38

5.1 - Implementação ................................... 38

5.2 - Simulação ....................................... 39

5.3 - Resultados ....................................... 40

6 Conclusões 44

Bibliografia 45

ix
A Código em Verilog 47

B Códigos Teste em Assembly 65

65
B.1 - Código de Teste 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

B.2 - Código de Teste 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

B.3 - Código de Teste 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

B.4 - Código de Teste 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

x
Lista de Figuras
2.1 – Estrutura de uma FPGA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.2 – Estrutura do desenvolvimento de projetos em FPGA . . . . . . . . . . . . . . . . . . 7

2.3 – Arquitetura CISC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.4 – Arquitetura RISC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.5 – Arquitetura Von Neumann . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.6 – Arquitetura Harvard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

4.1 – Entradas e Saídas do PIC16F84 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4.2 – Pinagem do PIC16F84 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

4.3 – Elementos e Arquitetura do PIC16F84 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

4.4 – Timer 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.5 – Diagrama de blocos do Timer 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

4.6 – Timer Watch Dog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4.7 – Diagrama de blocos do WDT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4.8 – Pre-Escalador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

4.9 – Pre-Escalador dentro do diagrama de blocos do Timer 0 e WDT . . . . . . . . . 23

4.10 – Controlador de Interrupção . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.11 – Lógica de Interrupção . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

4.12 – Diagrama de blocos da CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4.13 – Maquina de estados do Sub-Sistema de Controle . . . . . . . . . . . . . . . . . . . . 29

4.14 – Mapa da Memória de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4.15 – Gravação da memória de programa não volátil Flash . . . . . . . . . . . . . . . . 37

5.1 – Exemplo de tela do Block Memory Generator ...................... 39

5.2 – Resultado códigos de teste B.1 e B.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

5.3 – Resultado código de teste B.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

5.4 – Resultado código de teste B.4 ................................... 42

xi
Lista de Tabelas
3.1 – Comparação de núcleos abertos do PIC16F84 . . . . . . . . . . . . . . . . . . . . . . . 16

4.1 – Instruções do PIC16F84 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.2 – Condições de RESET para o PC e o Registrador STATUS . . . . . . . . . . . . . 26

4.3 – Condições de RESET para os Principais Registradores . . . . . . . . . . . . . . . . 27

4.4 – Valores de TO e PD para as diferentes condições de RESET . . . . . . . . . . . 27

4.5 – Registradores Especiais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

4.6 – Registrador STATUS (Endereços 03h, 83h) . . . . . . . . . . . . . . . . . . . . . . . . 33

4.7 – Registrador OPTION (Endereço 81h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.8 – Registrador INTCON (Endereços 0Bh, 8Bh) . . . . . . . . . . . . . . . . . . . . . . . . 34

4.9 – Registrador EECON1 (Endereço 88h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

xii
Capítulo 1

Introdução
Durante a primeira metade do século XX, os circuitos eletrônicos utilizavam
válvulas, o que tornava o desenvolvimento de sistemas digitais proibitivo. Em 1947,
John Bardeen e Walter Brattain construíram o primeiro transistor nos Laboratórios Bell.
Dez anos mais tarde, Jack Kilby na Texas Instruments percebeu o potencial de
miniaturização onde vários transistores poderiam ser construídos em uma única peça de
silício e em 1958, ele construiu o primeiro circuito flip-flop integrado com dois
transistores. Bardeen, Brattain e William Shockley ganharam o Prêmio Nobel de Física
em 1956 pela invenção do transistor. Kilby recebeu o Prêmio Nobel de Física em 2000
pela invenção do circuito integrado. [1]
Na década de 1960, MOSFETs começaram a entrar em produção. MOSFETs
oferecem uma grande vantagem, pois consomem quase zero de potência enquanto não
estão chaveando. Em 1963, Frank Wanlass, na empresa Fairchild Semicondutor,
descreveu as primeiras portas lógicas usando MOSFETs. Os gates da Fairchild
ganharam o nome de Complementary Metal Oxide Semiconductor (CMOS). Os
circuitos utilizavam transistores discretos, mas consumiam apenas nanowatts de
potência.
Processos CMOS foram amplamente adotados e substituíram essencialmente
processos bipolares para quase todas as aplicações em sistemas digitais e tornaram o seu
processo de implementação mais simples.

1.1 – Tema

Este é um trabalho que se insere nas áreas da Eletrônica e da Computação, mais


especificamente na subárea da Eletrônica Digital.
O tema do trabalho é o estudo da implementação de um sistema digital. Neste
sentido, pretende-se projetar um microcontrolador desde seu conceito base até o modelo
computacional do sistema proposto para implementá-lo em uma FPGA, para testes. A
hipótese inicial é que tendo o sistema bem documentado, implementado e validado a
nível lógico, é possível gerar o sistema funcional de forma semiautomática. Desta

1
forma, em havendo um sistema digital implementado em sua concepção e a nível lógico
(descrito em uma linguagem de descrição de hardware ou esquemático) será possível
realiza-lo tanto de maneira temporária em uma FPGA como em uma forma permanente
em um circuito integrado. [2]

1.2 – Delimitação

O objeto de estudo é o desenvolvimento e implementação de um sistema digital


para uma FPGA. Para que o desenvolvimento seja feito com base em uma concepção
madura do sistema proposto será tomado como base o PIC 16F84 [3] [4] da Microchip
Technology. O modelo computacional será voltado para implementação via linguagem
de descrição de hardware (HDL), e não no processo químico e industrial da
implementação do sistema.

1.3 – Justificativa

Através do curso de Engenharia Eletrônica e de Computação temos várias


disciplinas de eletrônica digital, porém em nenhuma delas temos uma visão geral de
todo o processo de desenvolvimento e implementação de um sistema digital. Mesmo
existindo a disciplina de Projeto Integrado, ela se propõe a implementação de projetos
interdisciplinares dentro da eletrônica e não de projetos em áreas específicas.
Neste sentido, o presente projeto é uma complementação dos estudos anteriores
no curso, buscando avançar na compreensão da implementação e do funcionamento de
sistemas digitais, segundo algumas condições de contorno. Sua originalidade reside no
fato de não existir uma documentação completa e aberta do sistema proposto. Assim, a
importância deste trabalho está relacionada com a possibilidade de aplicar as técnicas,
conceitos e resultados desta modelagem em outros sistemas digitais e na possibilidade
de gerar uma documentação clara do sistema proposto.
Outra possível aplicação do estudo desenvolvido neste trabalho seria a utilização
de microcontroladores em FPGAs para obter maior desempenho, devido a possibilidade
de utilizar maiores frequências de clock, ou pela possibilidade de integrar uma maior
parte do circuito desejado em uma único CI.

2
1.4 – Objetivos

O objetivo geral é, então, implemetar para uma FPGA um sistema digital


baseado no PIC 16F84.
Desta forma, tem-se como objetivos específicos: (1) Criar um modelo do PIC
16F84 em diagrama de blocos; (2) Construir um modelo em linguagem de descrição de
hardware capaz de ser implementado em uma FPGA; (3) Descrever o funcionamento do
PIC 16F84 como exemplo de uma CPU do tipo RISC. [5]

1.5 – Metodologia

Este trabalho irá utilizar diagramas de bloco e uma linguagem de descrição de


hardware (HDL) para modelar um sistema digital baseado no PIC 16F84 da Microchip
[3] [4]. A partir do uso da descrição em HDL, no caso Verilog, simular sua
implementação em uma FPGA para validação.
A maioria das empresas do ramo de criação de circuitos integrados (CIs) utiliza
modelos feitos em Verilog e VHDL (linguagens de descrição de hardware) para a
criação dos CIs.
Desta forma, através de ambientes de testes e simulação interativos baseados em
softwares de simulação em FPGA, deseja-se mensurar e analisar o funcionamento do
sistema proposto. Além disso, recentemente muitas empresas vêm trabalhando com
FPGAs em campo pela sua versatilidade em aplicações. [2]
Este trabalho está centrado na modelagem do sistema proposto tanto em blocos
como em HDL e na simulação de implementação em uma FPGA.

3
1.6 – Descrição

O capítulo 1 faz uma introdução ao tema do trabalho, especificando os objetivos


propostos, a metodologia e a descrição.
O capítulo 2 discute as tecnologias relevantes ao trabalho incluindo as utilizadas
no desenvolvimento do sistema apresentado.
O capítulo 3 faz uma revisão bibliográfica, analisa as principais implementações
encontradas que têm relação com o trabalho e justifica escolhas feitas na implementação
do sistema.
O capítulo 4 apresenta a concepção e o projeto do sistema desenvolvido.
O capítulo 5 descreve a implementação e os resultados do sistema desenvolvido.
O capítulo 6 apresenta as conclusões deste trabalho e propõe alguns temas para
evolução deste projeto.

4
Capítulo 2

Revisão Técnica

2.1 – FPGAs

Nas últimas décadas, o crescimento, tanto em variabilidade, quanto em


capacidade, dos dispositivos reconfiguráveis, e de suas respectivas ferramentas de
desenvolvimento, tem favorecido, em um curto espaço de tempo, a implementação de
sistemas complexos e repletos de lógica integrada e programável.
Empresas como Xilinx [6], Atmel [7] e Altera [8] são exemplos de empresas que
desenvolvem soluções na área de sistemas reconfiguráveis, tanto digitais, como
analógicos, ou ambos. Cada uma destas empresas também possui, as suas respectivas
ferramentas de desenvolvimento: ISE (Xilinx) [9], IDS e ProChip Designer (Atmel),
Quartus II e SOPC Builder (Altera).
A FPGA (do inglês Field-Programmable Gate Array) é um circuito integrado
projetado para ser configurado pelo cliente após a sua fabricação. A configuração da
FPGA é geralmente especificada utilizando HDL. A habilidade de atualizar suas
funcionalidades após a venda, de reconfigurar partes do projeto, e o baixo custo
oferecem vantagens para muitas aplicações.

2.1.1 – A estrutura de uma FPGA

Os três elementos básicos em uma FPGA são os Blocos de Lógica Configurável


(CLB), as interconexões, e os blocos de entrada/saída (E/S).
Em sua estrutura básica uma FPGA tem:
 Os blocos E/S em todo o perímetro da estrutura fornecendo acesso
selecionável de entrada, saída ou bidirecional, ao mundo externo.
 A matriz distribuída de interconexões programáveis fornecendo
interconexões aos CLBs e conexões aos blocos E/S.

5
A maioria dos fabricantes de dispositivos de lógica programável produz uma
série de FPGAs que variam em densidade, consumo de energia, tensão de alimentação,
tempo de resposta, e até certo ponto variam em arquitetura.
FPGAs são reprogramáveis e usam as tecnologias SRAM (volátil) ou antifuse
(não volátil) para programar as interconexões [10]. A densidade pode variar de centenas
de CLBs a centenas de milhares CLBs. A tensão de alimentação contínua varia
tipicamente entre 1.2V a 2.5V, dependendo do dispositivo.

Figura 2.1 – Estrutura de uma FPGA

Normalmente um CLB consiste de vários módulos lógicos menores que são as


unidades básicas de construção. A Figura 2.1 mostra um CLB dentro das interconexões
programáveis globais que são utilizadas para conectar os CLBs. Cada CLB é constituído
por múltiplos módulos lógicos menores e uma interconexão programável local que é
utilizada para conectar os módulos lógicos dentro do CLB. Um módulo lógico em um
CLB pode ser configurado para lógica combinacional, lógica sequencial, ou uma
combinação de ambos.

6
2.1.2 – Núcleo da FPGA

Existem dois tipos de núcleos da FPGA:


 Núcleo rígido - é uma parte de lógica em uma FPGA que é colocada pelo
fabricante para fornecer uma função específica, e não pode ser
reprogramada. Uma vantagem na utilização de núcleos rígidos é a utilização
de menos espaço na FPGA do que o mesmo projeto implementado em
campo por um usuário. Além disto, as funções do núcleo rígido já foram
exaustivamente testadas e não demandam tempo de desenvolvimento.
 Núcleo flexível - é uma parte de lógica que é programada pelo usuário e
pode ser modificada. A principal vantagem da utilização de núcleos flexíveis
está na sua flexibilidade de poder ser modificado. Projetos de núcleos
flexíveis podem ser encontrados prontos ou podem ser desenvolvidos.

2.1.3 – Desenvolvimento de projetos em FPGA

Independentemente do produto final, projetistas de FPGA seguem de uma forma


geral um mesmo processo básico. Os estágios de desenvolvimento de projetos em
FPGA são projeto, simulação, síntese e implementação, como mostrado na Figura 2.2.

Figura 2.2 – Estrutura do desenvolvimento de projetos em FPGA

7
2.1.3.1 – Projeto

O processo de projeto envolve a conversão dos requisitos para um formato que


representa a função digital desejada. Formatos de concepção mais comuns são o
esquemático, HDL ou uma combinação dos dois. Embora cada método tenha suas
vantagens e desvantagens, HDLs geralmente oferecem maior flexibilidade de projeto.
O esquemático é uma representação gráfica de um projeto digital, mostra a
interligação efetiva entre as portas lógicas que produzem as funções de saída desejadas.
Nesse tipo de projeto é necessário muitas vezes fazer toda a modelagem do sistema
dentro da ferramenta de projeto o que o torna dependente do fabricante da FPGA.
As HDLs utilizam código para representar funções digitais. Pode-se criar o
código-fonte com qualquer editor de texto, ou através de editores especiais como os
fornecidos pelas fabricantes de FPGAs citados na seção 2.1, que oferecem recursos
como modelos e destaque das palavras. As HDLs podem ser genéricas, como Verilog
ou VHDL, podendo ser utilizadas por várias ferramentas de desenvolvimento de
projeto, ou específicas, como o Verilog-A, que só é reconhecida por poucos softwares
dentre eles o Tanner EDA.

2.1.3.2 – Simulação

A simulação é o ato de verificar o projeto antes de implementá-lo no hardware


real para validação. As características dos sinais de entrada do circuito são descritos em
HDL ou graficamente, e então são aplicados ao projeto. Isso permite que o responsável
pela verificação do código possa observar o comportamento das saídas.
Na simulação, os sinais de entrada do circuito, também chamados de estímulos,
imitam sinais de entrada de um circuito real. Os estímulos forçam o circuito a operar
sob várias circunstâncias e estados. O maior benefício dos estímulos é a habilidade de
aplicar uma vasta gama de sinais com características válidas e não válidas. Assim,
podem-se variar parâmetros de sinais de entrada e testar os limites do circuito,
observando o comportamento das saídas sem causar dano ao hardware.

8
Existem três níveis de simulação: RTL, funcional e nível de porta. Cada um
ocorre em um lugar específico no processo de desenvolvimento. Geralmente, o estímulo
desenvolvido para a simulação RTL é reutilizável sem modificações para cada nível de
simulação.
 RTL - A simulação inicial realizada imediatamente após a fase de projeto é a
simulação RTL. Ela envolve diretamente a aplicação dos estímulos ao projeto. A
simulação RTL apenas permite que os projetistas verifiquem se a lógica está
correta.
 Funcional - A aplicação de estímulo de teste ao projeto sintetizado é uma
simulação funcional. Nesta simulação é verificado se o processo de síntese não
alterou o projeto.
 Nível de Porta - Na simulação em nível de porta, todos os atrasos de tempo
internos são incluídos, tornando as saídas da simulação mais precisas.

2.1.3.3 – Síntese

O processo de síntese é divido em três passos: (1) compilar o projeto em


elementos estruturais; (2) otimizar o projeto, removendo lógicas desnecessárias e
permitindo que os sinais cheguem às entradas ou saídas mais rapidamente; (3) Mapear
ou associar o projeto à arquitetura específica de uma FPGA.

2.1.3.4 – Implementação

A última fase no processo de desenvolvimento de projeto em FPGA é a


implementação, também conhecida como Place And Route (PAR), que também é
composta de três passos: (1) Tradução, que é a verificação da coerência entre a síntese
do projeto e a FPGA alvo; (2) Distribuição da lógica do projeto na FPGA; (3) Geração
do arquivo de programação, que pode ser gravado em uma memória flash, EEPROM ou
diretamente na FPGA, dependendo do fabricante.

9
2.2 – Microcontroladores

O desenvolvimento contínuo da microeletrônica, pode-se dizer hoje em dia até


nanoeletrônica, possibilitou que um número cada vez maior de transistores pudesse ser
integrado por unidade de área [1]. Em certo ponto do desenvolvimento, todos os blocos
básicos de um computador puderam ser integrados em uma única pastilha de silício,
dando origem aos microcontroladores.
Atualmente existem microcontroladores com processadores de 8, 16 e 32 bits, e
integram além das partes básicas de um microcomputador outras funções como módulos
USB, UART, comparadores, conversores A/D, PWM, osciladores internos, etc.
Por serem limitados com relação à memória de dados, os microcontroladores são
geralmente utilizados em aplicações específicas por sistemas embarcados como
telefones, forno de microondas, máquinas de lavar, elevadores, controladores de energia
elétrica, relógios de ponto e alarmes.
Existem dois tipos de arquiteturas de microprocessadores segundo sua estrutura
de instruções: (1) CISC; (2) RISC. E dois tipos de arquiteturas de microprocessadores
segundo sua estrutura de acesso à memória: (1) Von Neumann; (2) Harvard.
Quanto à estrutura de instruções, na arquitetura CISC existem muitas instruções
que podem ter tamanhos ou durações diferentes entre si, tornando os programas mais
simples. Um modelo de arquitetura CISC pode ser visto na Figura 2.3 e é evidenciado
pela memoria de controle que contêm as micro-instruções que compõe cada instrução.

Figura 2.3 – Arquitetura CISC

10
Quanto à estrutura de instruções, na arquitetura RISC, cada instrução tem o
mesmo tamanho e a mesma duração, com exceção das instruções que realizam desvio.
Por possuir poucas instruções, o programa torna-se mais complexo. Um modelo de
arquitetura RISC pode ser visto na Figura 2.4 e é evidenciado pela simplicidade do
sistema de controle que pode ser implementado com uma máquina de estados que
decodifica as instruções em sinais de controle para a CPU.

Figura 2.4 – Arquitetura RISC

Quanto à estrutura de acesso à memória, a arquitetura Von Neumann possui uma


única estrutura de armazenamento, que compreende as memórias de dados e de
programa, e as duas memórias compartilham os barramentos de dados e de endereço,
como pode ser visto na Figura 2.5. Como os barramentos são compartilhados, a CPU só
consegue ou ler uma instrução, ou ler/escrever um dado, limitando a largura de banda
de operação, o que é conhecido como Gargalo de Von Neumann.

11
Figura 2.5 – Arquitetura Von Neumann

A arquitetura Harvard utiliza memórias fisicamente separadas para instruções e


dados, requerendo barramentos de endereço e dados separados, como pode ser visto na
Figura 2.6, o que possibilita a CPU ler uma instrução e ler/escrever um dado
simultaneamente. Por possuir memórias fisicamente separadas, as características das
memórias de dados e programa podem ser diferentes, podendo ter barramentos de dados
e/ou endereço diferentes, temporizações diferentes e tecnologias diferentes.

Figura 2.6 – Arquitetura Harvard

O armazenamento separado de instruções e dados, como ocorre na arquitetura


Harvard, possibilita que as memórias de dados e instruções possuam palavras de
tamanhos diferentes, o que permite que uma instrução contenha um dado. A arquitetura
Harvard ainda permite a utilização de tipos diferentes de memória, pois instruções
podem ser armazenadas em memórias do tipo ROM, enquanto dados requerem que
possa ser feita escrita na memória então devem ser usadas EEPROM, FLASH ou RAM.
Geralmente microprocessadores são Harvard-RISC ou Von Neumann-CISC,
porém também podem existir Von Neumann-RISC e Harvard-CISC. Hoje em dia a

12
maioria dos microprocessadores são Harvard-RISC. Exemplos de microcontroladores e
microprocessadores Harvard-RISC são: o I7 da Intel e a família de microcontroladores
PIC da Microchip Technology.

13
Capítulo 3

Revisão Bibliográfica

3.1 – RISCuva1

O artigo em [11] foi apresentado na terceira FPGAworld Conference em


Estocolmo na Suécia em 2006 e apresenta o projeto de um microcontrolador em Verilog
chamado RISCuva1. Esse microcontrolador foi projetado para ser pequeno e rápido
executando uma instrução por ciclo de clock e implementado em uma FPGA.
No escopo do presente trabalho o RISCuva1 é interessante, pois apresenta a
existência de pesquisa e implementação de modelos de microcontroladores em FPGAs.

3.2 – PicoBlaze

O PicoBlaze [12] é o projeto de microcontrolador em VHDL e Verilog, criado e


otimizado pela Xilinx para ser implementado nas suas FPGAs. Atualmente, para as
FPGAs mais modernas a Xilinx recomenda a utilização do sucessor do PicoBlaze, o
MicroBlaze [13].
No escopo do presente trabalho o PicoBlaze e o MicoBlaze são interessantes,
pois apresentam a existência de implementações comerciais de modelos de
microcontroladores para FPGAs.

3.3 – Núcleos de código aberto

No site OpenCores foram encontrados os núcleos PPX16 [14] e risc16f84 [15].


Através da referencia do risc16f84 foi encontrado o núcleo CQPIC [16] e através do
projeto final de curso em [17] foi encontrado o núcleo UPEM [18] que implementam o
PIC16F84.

14
O núcleo PPX16 em [14] foi desenvolvido por Daniel Wallner e é uma
implementação em VHDL que possui duas versões, uma para o PIC16F84 e outra para
o PIC16C55. O núcleo foi publicado no site OpenCores em maio de 2002. A memória
ROM não foi especificada pelo autor. Enquanto a memória RAM foi especificada
pelo autor do núcleo implementada através de array.
O núcleo Risc16f84 e m [15] é uma implementação em Verilog, de um
microcontrolador PIC16F84 e possui quatro versões: risc16f84, risc16f84_lite,
risc16f84_small, risc16f84_clk2x. A única versão testada pelo autor é a versão
risc16f84_clk2x que não possui interface com EEPROM, interrupções ou PORTS. Um
barramento de dados auxiliar foi implementado para acesso de 64Kbytes de
registradores, PORTS e periféricos. As memórias RAM e ROM não foram
especificadas. O núcleo foi desenvolvido por John Clayton e é uma tradução para
Verilog do núcleo CQPIC publicado no site OpenCores em maio de 2002.
O núcleo CQPIC em [16] foi desenvolvido por Sumio Morioka e é uma
implementação em VHDL publicada em dezembro de 1999 na revista Transistor
Gijutsu, uma revista do Japão relacionada a hardware de computadores. O autor mantém
um site onde a última versão dos arquivos (v 1.00d) pode ser encontrada. A última
atualização foi feita em julho de 2004. O autor especificou as memórias RAM e ROM,
do tipo LPM [19], que somente são suportadas por FPGAs da Altera [9].
O núcleo UPEM e m [18] é uma simplificação do núcleo CQPIC em VHDL.
A memória ROM utilizada pelo autor foi implementada através de um case. Enquanto a
memória RAM foi especificada pelo autor do núcleo implementada através de array.
No escopo do presente trabalho os núcleos de código aberto são interessantes,
pois apresentam a existência de implementações abertas em HDL do modelo proposto
de microcontroladores para FPGAs.

3.4 – Conclusão da revisão bibliográfica

Após a verificação das implementações existentes pode-se validar a


importância do estudo e da pesquisa na implementação de sistemas digitas em HDL.
Entre os trabalhos analisados os apresentados nas seções 3.1 e 3.2 são otimizados para
implentação em FPGA e não seguem o modelo do PIC16F84, já os apresentados na

15
seção 3.3 foram implementados baseados em modelos do PIC16F84. Na tabela abaixo
é feita uma comparação entre as implementações estudadas na seção 3.3.

Tabela 3.1 – Comparação de núcleos abertos do PIC16F84


PIC 16F84 CQPIC risc16f84 PPX16 UPEM
Linguagem VHDL Verilog VHDL VHDL
WDT Sim Não Não Não
Sleep Sim Não Não Não
Timer 0 Sim Não Sim Não
Interrupções Sim Não Sim Não
Port A Sim Não 1) Sim 2) Não
Port B Sim Não 1) Sim Sim
Pilha Sim Sim Sim Sim
EEPROM Só conector Não Não Não
RAM Não 3) Não Sim Sim
ROM Não 3) Não Não Sim
Frequência de
99,59Mhz 93,8Mhz 56,09Mhz 109,7MHz
operação [17]
(1) Implementado como um port auxiliar (2) Port A com 8 bits ao invés de 5
(3) Implementado com LPM [19]

Dentre os núcleos de PIC16F84, o núcleo CQPIC aparenta ser o mais


interessante por conter todas as funções básicas do PIC16F84 e por isso foi usado como
base para a criação do código em Verilog deste trabalho.

16
Capítulo 4

Modelagem do Sistema

4.1 – Geral

Figura 4.1 – Entradas e Saídas do PIC16F84

O microcontrolador analisado neste trabalho é o PIC16F84 que possui 35


instruções, além de dois conjuntos de portas de E/S, Port A (5 bits) e Port B (8 bits), um
temporizador de uso geral (Timer 0) e um temporizador Watch Dog (WDT), ambos
compartilhando um pre-escalador, também possui um controlador de interrupções,
memória EEPROM de dados, memória Flash de programa, uma pilha para endereços de
retorno e um arquivo de registradores implementado usando memória RAM. A figura
4.1 mostra as entradas e saídas do microcontrolador enquanto a figura 4.2 mostra a
pinagem do mesmo.

17
Figura 4.2 – Pinagem do PIC16F84
Fonte: Manual PIC16F84 [3]

O microcontrolador PIC16F84 utiliza a arquitetura Harvard e contém um


conjunto de Instruções do tipo RISC. Na Figura 4.3 pode ser visualizada, de uma forma
geral, a estrutura do dispositivo e na Tabela 4.1 as 35 instruções implementadas nele.

Figura 4.3 – Elementos e Arquitetura do PIC16F84

18
Tabela 4.1 – Instruções do PIC16F84
14-Bit Opcode Flags
Mnemonicos Operandos Descrição Ciclos
MSb LSb Afetados

ADDWF f, d Soma W e f 1 00 0111 dfff ffff C,DC,Z


ANDWF f, d AND W com f 1 00 0101 dfff ffff Z
CLRF f Zera f 1 00 0001 lfff ffff Z
CLRW - Zera W 1 00 0001 0xxx xxxx Z
COMF f, d Complementa f 1 00 1001 dfff ffff Z
DECF f, d Decrementa f 1 00 0011 dfff ffff Z
DECFSZ f, d Decrementa f, pula se 0 1 (1) 00 1011 dfff ffff
INCF f, d Incrementa f 1 00 1010 dfff ffff Z
INCFSZ f, d Incrementa f, pula se 0 1 (1) 00 1111 dfff ffff
IORWF f, d OR W com f 1 00 0100 dfff ffff Z
MOVF f, d Move f 1 00 1000 dfff ffff Z
MOVWF f Move W para f 1 00 0000 lfff ffff
NOP - Nenhuma Operação 1 00 0000 0xx0 0000
RLF f, d Rotaciona para a esquerda f através do Carry 1 00 1101 dfff ffff C
RRF f, d Rotaciona para a direita f através do Carry 1 00 1100 dfff ffff C
SUBWF f, d Subtrai W de f 1 00 0010 dfff ffff C,DC,Z
SWAPF f, d Troca de nibbles no f 1 00 1110 dfff ffff
XORWF f, d XOR W com f 1 00 0110 dfff ffff Z
BCF f, b Zera Bit f 1 01 00bb bfff ffff
BSF f, b Seta Bit f 1 01 01bb bfff ffff
BTFSC f, b Testa Bit f, Pula se 0 1 (1) 01 10bb bfff ffff
BTFSS f, b Testa Bit f, Pula se 1 1 (1) 01 11bb bfff ffff
ADDLW k Soma literal e W 1 11 111x kkkk kkkk C,DC,Z
ANDLW k AND literal com W 1 11 1001 kkkk kkkk Z
CALL k Chama subrotina 2 10 0kkk kkkk kkkk
CLRWDT - Zera Watchdog Timer 1 00 0000 0110 0100 TO, PD
GOTO k Vai para o endereço 2 10 1kkk kkkk kkkk
IORLW k OR literal com W 1 11 1000 kkkk kkkk Z
MOVLW k Move literal para W 1 11 00xx kkkk kkkk
RETFIE - Retorna de interrupção 2 00 0000 0000 1001
RETLW k Retorna com literal em W 2 11 01xx kkkk kkkk
RETURN - Retorna de Subrotina 2 00 0000 0000 1000
SLEEP - Entra em modo standby 1 00 0000 0110 0011 TO, PD
SUBLW k Subtrai W de literal 1 11 110x kkkk kkkk C,DC,Z
XORLW k XOR literal com W 1 11 1010 kkkk kkkk Z
(1) Se o PC é modificado ou um teste condicional é verdadeiro, a instrução requer 2 ciclos.
Fonte: Manual PIC16F84 [3]

4.1.1 – Timer 0

Figura 4.4 – Timer 0

19
O Timer 0 é um contador/temporizador pre-setavel de 8 bits com seleção de
clock (interno ou externo) e pre-escalador programável de 8 bits como visto na Figura
4.4. Durante a execução do programa o valor do Timer 0 pode ser lido ou escrito a
qualquer momento. A figura 4.5 mostra o diagrama de blocos do Timer 0.

Figura 4.5 - Diagrama de blocos do Timer 0


Fonte: Manual PIC16F84 [3]

Existem dois modos de operação do Timer 0:


 Modo Temporizador - T0CS = 0 (Registrador OPTION [5]).
Neste modo, o Timer 0 incrementa todo ciclo de instrução (sem o pre-
escalador). Se houver escrita no registrador TMR0, o incremento é
inibido pelos dois ciclos seguintes.
 Modo Contador - T0CS = 1 (Registrador OPTION [5]).
Neste modo, o Timer 0 incrementa, ou na subida ou na descida do pulso
fornecido no pino RA4/T0CKI. A escolha da subida ou descida do pulso
é feita no T0SE (Registrador OPTION [4]).
A interrupção TMR0 é gerada quando o registrador TMR0 estoura passando de
FFh para 00h. Esse estouro seta o bit T0IF (Registrador INTCON[2]). A interrupção
pode ser mascarada zerando o T0IE (Registrador INTCON[5]). O bit T0IF precisa ser
zerado na rotina de interrupção do Timer 0 antes de reativar essa interrupção. Essa
interrupção não consegue acordar a CPU do modo SLEEP, pois o Timer 0 para durante
esse modo.

20
4.1.2 – Timer Watch Dog

Figura 4.6 – Timer Watch Dog

O Timer Watch Dog (WDT) é composto por um contador alimentado por um


oscilador RC embarcado que não necessita de nenhum componente. Esse oscilador é
completamente independente dos sinais de clock nos pinos OSC1/CLKIN e
OSC2/CLKOUT e por isso o WDT continuará funcionando mesmo que estes sinais
parem de ser recebidos, como por exemplo, durante a execução da instrução SLEEP. Na
Figura 4.6 pode se ter uma visão geral do WDT e Figura 4.7 mostra o diagrama de
blocos dele.

Figura 4.7 - Diagrama de blocos do WDT


Fonte: Manual PIC16F84 [3]

21
Durante o funcionamento normal, o estouro do WDT cria um RESET do PIC. O
bit TO (Registrador STATUS[4]) é zerado quando ocorre o estouro.
Se o PIC está no modo SLEEP, o estouro do WDT tem como consequência o
despertar do PIC e o retorno ao modo de funcionamento normal. As instruções
CLRWDT e SLEEP zeram o WDT e impedem-no de estourar gerando uma condição de
RESET.
O WDT pode ser desabilitado, permanentemente, programando o bit de
configuração WDTE como '0'.

4.1.3 – Pre-Escalador

Figura 4.8 – Pre-Escalador

Um contador de 8 bits é implementado como pre-escalador do Timer 0 ou do


WDT como pode ser visto na Figura 4.8. Por simplicidade, chamaremos esse contador
de “Pre-Escalador” neste trabalho. Não é possível utilizar o pre-escalador
simultaneamente para o Timer 0 e WDT, então a designação do pre-escalador ao Timer
0 implica na não existência do pre-escalador para o WDT e vice-versa. A figura 4.9
mostra o Pre-Escalador dentro do diagrama de blocos do Timer 0 e WDT.

22
Figura 4.9 – Pre-Escalador dentro do diagrama de blocos do Timer 0 e WDT
Fonte: Manual PIC16F84 [3]

Os bits PSA e PS2:PS0 (Registrador OPTION[3:0]) determinam a designação


do pre-escalador e sua taxa. A designação do pre-escalador pode ser alterada durante a
execução do programa.
 Com PSA=0 o pre-escalador será designado ao Timer 0.
Neste caso, os valores possíveis da taxa do pre-escalador são 1:2, 1:4, ... 1:256.
 Com PSA=1 o pre-escalador será designado ao WDT.
Neste caso, os valores possíveis da taxa do pre-escalador são 1:1, 1:2, ... 1:128.
Não é possível escrever nem ler o conteúdo do pre-escalador. Porém, quando
designado ao Timer 0, todas as instruções que escrevem no Timer 0 zerarão o pre-
escalador. Já quando designado ao WDT, a instrução CLRWDT o zerará.

23
4.1.4 – Interrupções

Figura 4.10 – Controlador de Interrupção

O PIC16F84A tem quarto possíveis fontes de interrupção: (1) Interrupção


Externa no pino RB0/INT; (2) Interrupção por estouro do TMR0; (3) Interrupções por
mudança de valores na PORTB (pinos RB7:RB4); (4) Interrupção por fim de escrita na
EEPROM. A figura 4.10 apresenta o controlador de interrupção.
O registrador de controle de interrupção (INTCON) armazena os pedidos de
interrupção individuais em bits de flag. O bit de habilitação global de interrupção (GIE,
global interrupt enable bit, INTCON[7]) habilita, se setado, todas as interrupções não
mascaradas ou desabilita, se zerado, todas as interrupções. O bit GIE é zerado quando
ocorre um RESET. Interrupções individuais podem ser desabilitadas por seus
respectivos bits de habilitação no registrador INTCON.
As interrupções do pino RB0/INT, das variações nos pinos RB e do estouro do
TMR0 são armazenadas no registrador INTCON, enquanto a interrupção de finalização
de escrita da EEPROM é armazenada no bit EEIF do registrador EECON1. A figura
4.11 apresenta a lógica de interrupção.

24
Figura 4.11 – Logica de Interrupção

Quando ocorre uma interrupção, o GIE é zerado para desabilitar novas


interrupções, o endereço de retorno é guardado na pilha e o PC é carregado com o valor
0004h. Durante uma interrupção somente o valor do endereço do PC de retorno é
guardado. A instrução que retorna da interrupção, RETFIE, provoca a saída da rotina de
interrupção e seta o GIE, o que reabilita as interrupções. Quando ocorre uma
interrupção é possível determinar a causa verificando os flags de interrupção descritos
na seção 4.3.1.1. Os flags de interrupção precisam ser zerados no programa antes de
reativar as interrupções.

4.1.5 – Portas

O PIC16F84 tem duas portas de E/S PORTA e PORTB. Alguns pinos destas
portas são multiplexados com funções alternativas, que quando configuradas como
ativas não permitem o uso desses pinos para E/S. Ao se ler o registrador da porta na
verdade se lê o status dos pinos, mas ao se escrever, escreve-se somente no registrador.
Então a escrita implica na leitura por software do estado atual da porta, modificação
deste estado e gravação no registrador.
A PORTA é uma porta bidirecional com cinco bits e seu registrador de direção
correspondente é o TRISA. O pino RA4 é multiplexado com a entrada de clock do
Timer 0 e forma o pino RA4/T0CKI.

25
A PORTB é uma porta bidirecional com oito bits e seu registrador de direção
correspondente é o TRISB. Cada pino da PORTB quando configurado como entrada
tem um pull-up interno, que são controlados pelo bit RBPU (OPTION[7]). O pull-up é
desabilitado quando ocorre o Power-on Reset. Quatro pinos da PORTB, RB7:RB4, se
configurados como entrada geram uma interrupção quando alteradas.
O ato de setar um bit de TRISA/B fará com que o bit correspondente da
respectiva porta funcione como uma entrada e zerando-o fará com que ele funcione
como uma saída.

4.1.6 – Condições de Reset

O PIC16F84 diferencia entre cinco tipos de RESET: (1) Power-on Reset (POR);
(2) MCLR durante a operação normal; (3) MCLR durante o modo SLEEP; (4) Reset por
estouro do WDT (durante a operação normal); (5) WDT Wake-up (durante o modo
SLEEP).
Alguns registradores não são afetados em nenhum tipo de RESET, porém a
maioria dos registradores mudam seus valores para os valores do “Estado de RESET”
nos resets dos tipos 1, 2, 3 e 4, e não são alterados no reset do tipo 5, pois esse é
simplesmente a volta ao modo normal de operação. A tabela 4.2 descreve a informação
no PC e no registrador STATUS para cada tipo de reset, enquanto a tabela 4.3 descreve
a informação nos principais registradores para cada tipo de RESET.

Tabela 4.2 – Condições de RESET para o PC e o Registrador STATUS


No Condition PC STATUS
1 Power-on Reset 000h 0001 1xxx
Register
2 MCLR durante a operação normal 000h 000u uuuu
3 MCLR durante o modo SLEEP 000h 0001 0uuu
4 Reset por estouro do WDT 000h 0000 1uuu
5 WDT Wake-up PC + 1 uuu0 0uuu
- Wake-up por Interrupção (do modo SLEEP) PC + 1 uuu1 0uuu
Legenda: u = não modificado; x = desconhecido
Fonte: Manual PIC16F84 [3]

26
Tabela 4.3 – Condições de RESET para os Principais Registradores
Registrador Endereço 1 2, 3 e 4 5
W — xxxx xxxx uuuu uuuu uuuu uuuu
TMR0 01h xxxx xxxx uuuu uuuu uuuu uuuu
PCL 02h/82h 0000 0000 0000 0000 PC +1
STATUS 03h/83h 0001 1xxx 000q quuu uuuq quuu
FSR 04h/84h xxxx xxxx uuuu uuuu uuuu uuuu
PORTA 05h ---x xxxx ---u uuuu ---u uuuu
PORTB 06h xxxx xxxx uuuu uuuu uuuu uuuu
EEDATA 08h xxxx xxxx uuuu uuuu uuuu uuuu
EEADR 09h xxxx xxxx uuuu uuuu uuuu uuuu
PCLATH 0Ah/8Ah ---0 0000 ---0 0000 ---u uuuu
OPTION 81h 1111 1111 1111 1111 uuuu uuuu
TRISA 85h ---1 1111 ---1 1111 ---u uuuu
TRISB 86h 1111 1111 1111 1111 uuuu uuuu
EECON1 88h ---0 x000 ---0 q000 ---0 uuuu
Legenda: u = não modificado; x = desconhecido; - = não implementado; q = valor
depende de uma condição.

Os bits TO e PD bits indicam os diferentes tipos de RESET e são utilizados para


determinar qual ocorreu. Na tabela 4.4 é possível ver a relação entre esses bits e as
condições de RESET.

Tabela 4.4 – Valores de TO e PD para as diferentes condições de RESET


TO PD Condição de RESET
1 1 1e2
1 0 3
0 1 4
0 0 5

O pulso de Power-on Reset (POR) é gerado no CI quando um aumento de VDD é


detectado. Para utilizá-lo é necessário associar o pino do MCLR ao VDD através de um
circuito externo. O pulso de POR não é gerado quando VDD diminui.

27
4.2 – CPU

A CPU do PIC16F84 apresenta duas subdivisões: o sub-sistema de controle e o


sub-sistema de dados. A figura 4.12 apresenta o diagrama de blocos da CPU com seus
periféricos.

Figura 4.12 - Diagrama de blocos da CPU


Fonte: Manual PIC16F84 [3]

4.2.1 – Sub-Sistema de Controle

Por ser um microcontrolador do tipo RISC o sub-sistema de controle da CPU é


implementado como uma máquina de estados onde os estados Q1 a Q4 representam um
ciclo de instrução que contem 4 pulsos de clock e o estado Qreset é o estado atingido na
inicialização ou quando ocorre uma das condições de reset. Não é claro se o controle
exercido por esse sub-sistema é decodificado, porém neste nível de modelagem essa

28
especificação não é necessária. A figura 4.13 apresenta de forma simplificada a máquina
de estados do sub-sistema de controle da CPU.

Figura 4.13 – Máquina de estados do Sub-Sistema de Controle

No estado Qreset é feita a inicialização dos registradores. No estado Q1 é feita a


decodificação da instrução, leitura das portas e iniciada as operações com a EEPROM.
Já no estado Q2 são feitas as atribuições dos operandos para as operações aritméticas e
lógicas e no estado Q3 é feita a operação aritmética ou lógica, o incremento do Timer 0
e a verificação da existência de uma interrupção. O estado Q4 gera o endereço da
próxima instrução, atribuí o resultado da operação ao seu destino e atribuí o valor dos
flags.

29
4.2.2 – Sub-Sistema de Dados

O Sub-Sistema de dados da CPU é muito simples e inclui muitos dos itens


apresentados na seção 4.1 como o WDT, o Timer 0 e o controlador de interrupções.
Os principais componentes do Sub-Sistema de dados da CPU que ainda não
foram apresentados são a Unidade Aritmética Lógica (ALU), Registrador de Trabalho
(W), o Program Counter (PC) e a pilha.
A ALU do PIC16F84 somente efetua operações simples com o auxilio do
registrador W. O PC é utilizado no endereçamento da memória de programa, e seu
conteúdo é armazenado, automaticamente, na pilha quando ocorre uma interrupção ou é
chamada uma rotina.

4.3 – Memórias

O PIC16F84 apresenta três conjuntos diferentes de memórias, um arquivo de


registradores, implementado utilizando um bloco de memória RAM, uma memória não
volátil para dados, implementada utilizando uma EEPROM e uma memória não volátil
para o programa, implementada utilizando uma memória Flash.

4.3.1 – Arquivo de Registradores (RAM)

O arquivo de registradores é implementado usando memória RAM e é dividido


por função em duas partes: (1) Special Function Registers (SFR); (2) General Purpose
Registers (GPR). Os SFRs controlam a operação do PIC.
As instruções MOVWF e MOVF movem os valores entre o registrador W e
qualquer posição no arquivo de registradores (“F”) e vice-versa.
Todo o arquivo de registradores pode ser acessado através do endereçamento
direto ou indireto, usando o File Select Register (FSR) e o valor do bit RP0.
O arquivo de registradores é dividido em dois bancos que contêm SFRs e GPRs.
A seleção do banco ocorre no registrador STATUS. Cada Banco se estende por 7Fh

30
(128 bytes), os doze primeiros bytes de cada banco são reservados para os SFRs e o
resto é composto por GPRs. A Figura 4.14 mostra a organização do arquivo de
registradores.

Figura 4.14 – Mapa da Memória Volátil de Dados


Fonte: Manual PIC16F84 [3]

Cada GPR tem oito bits e pode ser acessado diretamente ou indiretamente,
através do FSR. Os GPRs podem ser acessados através dos dois bancos, então, por
exemplo, 0Ch e 8Ch acessam o mesmo GPR.

31
4.3.1.1 – Registradores Especiais

Os registradores especiais são usados pela CPU e pelos periféricos para fazer o
controle do PIC. Esses registradores são implementados no Arquivo de Registradores.
As tabelas 4.2-4.6 especificam esses registradores.

Tabela 4.2 – Registradores Especiais


Valor no
End. Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 Power-on
RESET
Banco 0
00h INDF Usa o conteúdo do FSR para endereçar a memória de dados (não físico) ---- ----
01h TMR0 Contador/Temporizador de 8 bits (Timer 0) xxxx xxxx
02h PCL 8 bits menos significativos do PC 0000 0000
03h STATUS IRP RP1 RP0 TO PD Z DC C 0001 1xxx
04h FSR Ponteiro 0 de Endereçamento indireto da memória de dados xxxx xxxx
05h PORTA — — — RA4/T0CKI RA3 RA2 RA1 RA0 ---x xxxx
06h PORTB RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0/IN xxxx xxxx
07h — — T —
08h EEDATA Registrador de dados da EEPROM xxxx xxxx
09h EEADR Registrador de endereços da EEPROM xxxx xxxx
0Ah PCLATH — — — Buffer de escrita dos 5 MSB do PC ---0 0000
0Bh INTCON GIE EEIE T0IE INTE RBIE T0IF INTF RBIF 0000 000x
Banco 1
80h INDF Usa o conteúdo do FSR para endereçar a memória de dados (não físico) ---- ----
81h OPTION RBPU INTEDG T0CS T0SE PSA PS2 PS1 PS0 1111 1111
82h PCL 8 bits menos significativos do PC 0000 0000
83h STATUS IRP RP1 RP0 TO PD Z DC C 0001 1xxx
84h FSR Ponteiro 0 de Endereçamento indireto da memória de dados xxxx xxxx
85h TRISA — — — Registrador da direção dos dados da PORTA ---1 1111
86h TRISB Registrador da direção dos dados da PORTB 1111 1111
87h — — —
88h EECON1 — — — EEIF WRER WREN WR RD ---0 x000
89h EECON2 R
Registrador de controle 2 da EEPROM (não físico) ---- ----
0Ah PCLATH — — — Buffer de escrita dos 5 MSB do PC ---0 0000
0Bh INTCON GIE EEIE T0IE INTE RBIE T0IF INTF RBIF 0000 000x
Legenda: x= desconhecido; — = não implementado, se lê como '0'.
Fonte: Manual PIC16F84 [3]

32
Tabela 4.3 – Registrador STATUS (Endereços 03h, 83h)
U U R/W-0 R-1 R-1 R/W-x R/W-x R/W-x
IRP RP1 RP0 TO PD Z DC C
bit 7 bit 0
bit 5 RP0: Bits de seleção do Banco de Registradores (endereçamento direto)
01= Banco 1 (80h - FFh)
00= Banco 0 (00h - 7Fh)
bit 4 TO: Bit de Estouro
1= Depois de power-up ou das instruções CLRWDT ou SLEEP
0= Um Estouro de WDT aconteceu
bit 3 PD: Bit de Power-down
1 = Depois do power-up ou pela instrução CLRWDT
0 = Pela execução da instrução SLEEP
bit 2 Z: Flag de Zero
1 = O resultado de um calculo ou operação logica é zero
0 = O resultado de um calculo ou operação logica não é zero
bit 1 DC: Flag de Carry/Borrow Digital [ADDWF, ADDLW,SUBLW,SUBWF]
(o borrow tem polaridade revertida)
1 = Houve Carry no bit 4 do resultado do somador
0 = Não houve Carry no bit 4 do resultado do somador
bit 0 C: Flag de Carry/Borrow [ADDWF, ADDLW,SUBLW,SUBWF]
(o borrow tem polaridade revertida)
1 = Houve Carry no bit mais significativo do resultado do somador
0 = Não houve Carry no bit mais significativo do resultado do somador
Fonte: Manual PIC16F84 [3]

Tabela 4.4 – Registrador OPTION (Endereço 81h)


R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1
RBPU INTEDG T0CS T0SE PSA PS2 PS1 PS0
bit 7 bit 0
bit 7 RBPU: Bit de Habilitação do pull-up da PORTB
1 = PORTB pull-up está desabilitado
0 = PORTB pull-up está habilitado por valores individuais da porta
bit 6 INTEDG: Bit de Seleção de Borda de Interrupção
1 = Interrupção na subida do sinal do bit RB0/INT
0 = Interrupção na descida do sinal do bit RB0/INT
bit 5 T0CS: Bit de seleção de origem do Clock do TMR0
1 = Transição baseada no sinal do bit RA4/T0CKI
0 = Ciclo de Clock de instrução Interno (CLKOUT)
bit 4 T0SE: Bit de seleção da borda do clock externo do TMR0
1 = Incrementa na descida do sinal do bit RA4/T0CKI
0 = Incrementa na subida do sinal do bit RA4/T0CKI

33
bit 3 PSA: Bit de designação do Pre-Escalador

1 = Pre-Escalador designado ao WDT


0 = Pre-Escalador designado ao Timer 0
bit 2-0 PS2:PS0: Bits de seleção da taxa do Pre-Escalador
Valor Taxa Taxa
Bits TMR0 WDT
000 1:2 1:1
001 1:4 1:2
010 1:8 1:4
011 1 : 16 1:8
100 1 : 32 1 : 16
101 1 : 64 1 : 32
110 1 : 128 1 : 64
111 1 : 256 1 : 128
Fonte: Manual PIC16F84 [3]

Tabela 4.5 – Registrador INTCON (Endereços 0Bh, 8Bh)


R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-x
GIE EEIE T0IE INTE RBIE T0IF INTF RBIF
bit 7 bit 0
bit 7 GIE: Bit de Habilitação Global de Interrupção
1 = Habilita todas as interrupções não mascaradas
0 = Desabilita todas as interrupções
bit 6 EEIE: Bit de Habilitação de Interrupção de finalização de escrita da EEPROM
1 = Habilita a interrupção de finalização de escrita da EEPROM
0 = Desabilita a interrupção de finalização de escrita da EEPROM
bit 5 T0IE: Bit de Habilitação de Interrupção de estouro TMR0
1 = Habilita a interrupção do TMR0
0 = Desabilita a interrupção do TMR0
bit 4 INTE: Bit de Habilitação de Interrupção Externa de RB0/INT
1 = Habilita a interrupção externa do RB0/INT
0 = Desabilita a interrupção externa do RB0/INT
bit 3 RBIE: Bit de Habilitação da interrupção de variações nas Portas RB
1 = Habilita a interrupção de variações nas Portas RB
0 = Desabilita a interrupção de variações nas Portas RB
bit 2 T0IF: Bit de Flag de estouro TMR0
1 = Houve estouro no TMR0 (deve ser zerado por software)
0 = Não houve estouro no TMR0
bit 1 INTF: Bit de Flag de interrupção externa do RB0/INT
1 = A interrupção externa do RB0/INT aconteceu (deve ser zerado por software)
0 = A interrupção externa do RB0/INT não aconteceu

34
bit 0 RBIF: Bit de Flag da interrupção de variações nas Portas RB
1 = Pelo menos um dos valores dos pinos RB7:RB4 se modificou (deve ser zerado por
software)
0 = Nenhum dos valores dos pinos RB7:RB4 se modificou
Fonte: Manual PIC16F84 [3]

Tabela 4.6 – Registrador EECON1 (Endereço 88h)


U-0 U-0 U-0 R/W-0 R/W-x R/W-0 R/S-0 R/S-x
— — — EEIF WRERR WREN WR RD
bit 7 bit 0
bit 7-5 Não Implementado: Lê-se comos '0'
bit 4 EEIF: Bit de Flag de Interrupção da operação de escrita da EEPROM
1 = Operação de escrita concluída, deve ser zerado por software
0 = Operação de escrita não concluída ou não iniciada
bit 3 WRERR: Bit de Flag de Erro da EEPROM
1 = A escrita foi interrompida
0 = A escrita foi completada
bit 2 WREN: Bit de Habilitação de escrita na EEPROM
1 = Permite o ciclo de escrita
0 = Inibe o ciclo de escrita
bit 1 WR: Bit de Controle de Escrita (não pode ser zerado por software)
1 = Inicia o ciclo de escrita da EEPROM, é zerado por hardware
0 = Ciclo de escrita da EEPROM completo
bit 0 RD: Bit de Controle de Leitura (não pode ser zerado por software)
1 = Inicia a leitura da EEPROM, é zerado por hardware
0 = Não inicia a leitura da EEPROM
Fonte: Manual PIC16F84 [3]

4.3.2 – EEPROM

A memória de dado não volátil implementada no PIC16F84 é do tipo EEPROM


e pode ser lida e escrita durante a operação normal. O PIC16F84A tem a capacidade de
armazenar 64 bytes de dados na EEPROM com endereços variando de 0h a 3Fh. Ao
invés de ser mapeada da mesma forma que o arquivo de registradores, essa memória é
mapeada indiretamente através de quatro SFRs: (1) EECON1; (2) EECON2; (3)
EEDATA; (4) EEADR.

35
EEDATA guarda 8 bits de dados para leitura/escrita, e EEADR guarda o
endereço da EEPROM a ser acessado.
Para ler os dados de uma posição da memória o programa deve escrever seu
endereço no registrador EEADR e setar o bit RD (EECON1[0]). Os dados estarão
disponíveis no próximo ciclo de clock no registrador EEDATA e será mantido lá até
que ocorra uma nova operação utilizando a EEPROM.
Para escrever dados em uma posição da memória EEPROM o programa deve
inicialmente setar o bit WREN no EECON1 para habilitar a escrita, esse bit não é
zerado automaticamente por hardware, em seguida deve escrever o endereço no
registrador EEADR, escrever os dados no registrador EEDATA, escrever 55h no
EECON2, escrever AAh no EECON2 e finalmente setar o bit WR (EECON1[1]).
Zerar o bit WREN no meio do ciclo de escrita não afeta o ciclo. O bit WR não é
setavel a menos que o bit WREN esteja setado. Quando a operação de escrita é
concluída o bit WR é zerado por hardware e o flag EEIF é setado.
Essa complexidade de escrita existe para impedir escritas acidentais na
EEPROM, pois escrever na EEPROM, automaticamente, sobrescreve o que estava
escrito anteriormente naquele endereço. Pelo mesmo motivo deve-se, sempre que não
estiver ocorrendo escrita na EEPROM, manter o bit WREN zerado.

4.3.3 – FLASH

A memória de programas do PIC16F84 é implementada como uma memória


Flash de 1kx14b. Enquanto o endereço 0000h é reservado para inicialização/reset, o
endereço 0004h é reservado para as rotinas de interrupção. Não é possível fazer a escrita
nessa memória durante a operação normal, para isso é necessário alimentar o PIC com
14V no pino MCLR como pode-se ver na figura 4.15

36
Figura 4.15 – Gravação da memória de programa não volátil Flash

Durante o processo de gravação ainda é possível escrever no registrador de


configuração que contem os bits de proteção do código, habilitação do Power-up Timer,
habilitação do WDT e seleção do tipo de oscilador.

37
Capítulo 5

Implementação e Resultados

5.1 – Implementação

O desenvolvimento do código da CPU do microcontrolador implementado foi


realizado inicialmente na IDE Komodo Edit 8 [20]. Posteriormente para complementar
o desenvolvimento do sistema o código foi transferido para o ISE [9].
A CPU foi implementada, seguindo o exemplo do CQPIC [16], tendo como base
o sub-sistema de controle. Assim o corpo do código é de uma maquina de estados
finitos de 5 estados (4 operacionais e 1 de reset). Foram implementadas as 35 instruções
da Tabela 4.1. Quando havia uma dúvida de como modelar e implementar algum dos
componentes existentes no sistema de referência foram utilizados como base o manual
em [3], o livro em [4] e o núcleo CQPIC [16]. O código implementado pode ser
encontrados no Anexo A.
O código da CPU foi dividido em sete partes: (A) Inicializações; (B) Funções e
Tarefas; (C) Nós intermediários; (D) Autômato Finito; (E) TMR0 e WDT; (F)
Interrupções; (G) Saídas.
Foi criado um arquivo em Verilog que relaciona o núcleo do sistema às
memórias com o nome top.v que não foi incluído no Anexo A por somente conter este
relacionamento.
As portas A e B foram implementadas com os bits separados de entrada e saída
para facilitar o acesso no contexto da FPGA, porém a nível do arquivo top.v essas
portas podem ser facilmente atribuídas a uma porta bidirecional com poucas linhas de
código. O registrador de configuração não foi implementado, pois somente um de seus
bits foi inteiramente implementado (o WDTE) e por isso esse bit foi modelado como
uma entrada do sistema.
A memória de programa foi implementada como uma ROM, pois o modo de
gravação da FLASH não foi implementado e durante a operação normal do sistema não
há alteração no conteúdo desta memória. A memória de dados EEPROM não foi
implementada devido à limitações em acesso a esse tipo de memória em FPGAs, porém

38
seus sinais e conectores foram implementados na CPU. Uma solução seria implementar
a EEPROM como uma RAM e para tanto seria necessário uma pequena seção de código
no arquivo top.v que fizesse a interface entre a CPU e a memória pela necessidade de
não mudar a estrutura dos sinais de acesso à EEPROM da CPU.
Todas as memórias foram implementadas usando o Block Memory Generator
que é parte integrante do ISE [9] da Xilinx para ser possível otimizar o espaço ocupado
pelo código no circuito da FPGA. A figura 5.1 mostra uma das telas do Block Memory
Generator.

Figura 5.1 – Exemplo de tela do Block Memory Generator

5.2 – Simulação

A metodologia de simulação e verificação do sistema implementado é composta


das seguintes etapas: (1) Compilação do núcleo; (2) Geração do programa de
verificação; (3) Associação do programa de verificação à memória ROM; (4)
Implementação do sistema com o Test Bench; (5) Análise.
Após a compilação do núcleo, um programa era escrito em assembly, no
MPLAB [21], para verificar se uma instrução ou função estava funcionando

39
corretamente. Não foram testadas todas as funções e instruções, pois seria necessária
uma bateria de testes exaustiva que testasse todas as condições possíveis, portanto
foram feitos testes para exemplificar o funcionamento da implementação. Os programas
de teste utilizados podem ser encontrados no Anexo B.
O programa na seção B.1 testa as instruções GOTO, MOVLW, MOVWF, INCF,
SUBWF e BTFSS e a função de E/S da PORTB.
O programa na seção B.2 testa as instruções GOTO, MOVLW, CLRF, INCF,
ANDWF e BTFSS e a função de E/S da PORTB.
O programa na seção B.3 testa as funções do controlador de interrupções, do
Timer 0 e E/S da PORTB.
O programa na seção B.4 testa as instruções GOTO, MOVLW, MOVWF,
CLRF, BCF, BSF e RLF e a função de E/S da PORTB.
No MPLAB deve ser selecionado como microcontrolador alvo, o PIC16F84. Ao
compilar o programa de verificação no MPLAB, é gerado um arquivo .hex, no formato
Intel HEX 32.
No Block Memory Generator, na definição dos atributos da memória ROM a ser
gerada, é possível associar à memória um arquivo contendo as instruções que estarão na
memória. Este arquivo deve ser de extensão .coe que possui uma formatação específica.
Para que a CPU execute o programa de verificação, é preciso converter o
arquivo .hex, gerado na compilação em um arquivo .coe, e então associar este arquivo à
memória ROM. A conversão foi feita através do programa hex2coe [22] escrito em
linguagem C.
Os Test Bench utilizados foram basicamente os mesmos para todos os quatro
programas de teste.
Os resultados da etapa de analise serão descritos na seção 5.3.

5.3 – Resultados

Nas simulações executadas houve êxito nas funções e instruções testadas. Em


cada uma das simulações houve um tempo de inicialização do sistema que variava
conforme o código de teste.
Na figura 5.2 podem-se ver os principais bits que evidenciam esse sucesso dos
dois primeiros programas (B.1 e B.2). Esses programas tinham como objetivo testar

40
tanto as operações logicas como as aritméticas da CPU. Olhando externamente a
indicação que o programa, com todos os seus componentes, funcionou é constatando
que o conteúdo do registrador W foi transferido para a PORTB.
Na figura 5.3 podem-se ver os principais bits que evidenciam esse sucesso do
terceiro programa (B.3). Esse programa tinha como objetivo testar as funções
periféricas do sistema e, portanto, utilizava a interrupção do Timer 0 para alterar os
dados no PORTB. Olhando externamente a indicação que o programa, com todos os
seus componentes, funcionou é que o conteúdo do PORTB passou a oscilar com uma
frequência fixa após um tempo de inicialização.
Na figura 5.4 podem-se ver os principais bits que evidenciam esse sucesso do
quarto programa (B.4). Esse programa tinha como objetivo testar as operações de
rotação (RRF/RLF) da CPU. Olhando externamente a indicação que o programa, com
todos os seus componentes, funcionou é que o conteúdo do PORTB rotacionou através
do carry a uma taxa fixa após um tempo de inicialização.

41
Figura 5.2 – Resultado códigos de teste B.1 e B.2

Figura 5.3 – Resultado código de teste B.3

42
Figura 5.4 – Resultado código de teste B.4

43
Capítulo 6

Conclusões

O sistema elaborado em HDL mantém compatibilidade com as instruções e


endereços do sistema de referência o que o torna versátil na implementação de
programas previamente desenvolvidos para esse sistema em uma FPGA. Por se basear
em um sistema implementado de forma permanente em um circuito integrado, o sistema
elaborado não é otimizado para trabalhar em uma FPGA.
Com este trabalho, foi dado um passo inicial no desenvolvimento de uma CPU
em HDL. O sistema foi elaborado de forma a ser ampliado de modo simples e efetivo.
Caso novas técnicas sejam implementadas, estas podem ser facilmente incluídas, devido
à utilização de um modelo genérico de microcontrolador, permitindo assim a constante
evolução deste trabalho.
Por fim, deve-se dizer que este trabalho não deve ser visto como algo acabado.
Da mesma forma que a tecnologia está sempre em desenvolvimento, cabe aos futuros
desenvolvedores interessados em dar continuidade a este trabalho, implementar novas
técnicas para a otimização do núcleo gerado e complementa-lo com módulos adicionais.
Trabalhos futuros podem abordar os seguintes pontos:
 Avaliar a melhor forma de implementar as memórias;
 Verificar o funcionamento do sistema desenvolvido neste trabalho em
compiladores e FPGAs de outros fabricantes;
 Otimizar o sistema para uma família de FPGAs;
 Implementar a EEPROM, a Flash e o processo de gravação;
 Utilizar o sistema em aplicações complexas, como controladores USB, I2C e
Ethernet;
 Implementar novas instruções para o sistema;
 Agregar novos módulos como PWM, UART, comparadores, conversores
A/D.

44
Bibliografia

[1] HARRIS, David Money; WESTE, Neil H. E.. “CMOS VLSI Design - A Circuits
and Systems Perspective”. 4th Edition, Addison-Wesley Pearson, 2011.

[2] WILSON, Peter R.. “Design recipes for FPGAs: Field programmable gate arrays –
Design and construction”. Elsevier Linacre House, 2011.

[3] PIC16F84A. Disponível em


<http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010230>.
Acesso em: 21/01/2015.

[4] SILVA JUNIOR, Vidal Pereira da. “Microcontroladores PIC: Teoria e Prática”.
2000.

[5] BRAFMAN, José Paulo. Slides da disciplina Arquitetura de Computadores, UFRJ.


2012.

[6] Xilinx Inc. Diponível em <www.xilinx.com> Acesso em: 22/12/2014.

[7] Atmel Corporation. Diponível em <www.atmel.com>. Acesso em: 22/12/2014.

[8] Altera Corporation. Disponível em <www.altera.com>. Acesso em: 22/12/2014.

[9] ISE, Xilinx Inc. Diponível em < http://www.xilinx.com/products/design-tools/ise-


design-suite.html> Acesso em: 20/07/2015.

[10] Design Security in Nonvolatile Flash and Antifuse FPGAs. Disponível em


<http://www.design- reuse.com/articles/6529/design-security-in-nonvolatile-flash-and-
antifuse-fpgas.html>. Acesso em: 01/02/2015.

[11] PABLO, S. de; CEBRIÁN, J.A.; HERRERO L.C.. A very simple 8-bit RISC
processor for FPGA. 3rd FPGAworld Conference, Estocolmo, Suecia, Novembro 2006.
Disponível em <http://www.dte.eis.uva.es/Datos/Congresos/FPGAworld2006a.pdf>>.
Acesso em: 01/08/2014.

[12] PicoBlaze 8-bit Microcontroller. Disponível em


<http://www.xilinx.com/products/intellectual-property/picoblaze.html> Acesso em:
01/08/2014.

[13] MicroBlaze Soft Processor Core. Disponível em


<http://www.xilinx.com/tools/microblaze.htm> Acesso em: 01/08/2014.

[14] PPX16 mcu. Disponível em


<http://www.opencores.org/?do=project&who=ppx16>. Acesso em: 21/01/2015.

45
[15] risc16f84. Disponível em
<http://www.opencores.org/?do=project&who=risc16f84>. Acesso em: 21/01/2015.

[16] CQPIC. Disponível em <http://www002.upp.so-net.ne.jp/morioka/cqpic.html>.


Acesso em: 21/01/2015.

[17] WILLIAM, Barcellos. Análise da implementação de núcleos de código aberto de


microcontroladores PIC16 em FPGA. USP, São Carlos, 2010. Projeto de final de curso.
Diponível em <http://www.tcc.sc.usp.br/tce/disponiveis/18/180450/tce-18112011-
113618/publico/Barcellos_William.pdf>. Acesso em: 21/01/2015.

[18] MORENO, E. M.; PENTEADO, C. G.; RODRIGUES, A. C. Microcontroladores e


FPGAs: Aplicações em automação. São Paulo: Novatec Editora Ltda., 2005, Capítulo
15. Disponível em <http://novatec.com.br/livros/microfpga/capitulo8575220799.pdf>.
Acesso em: 01/02/2015.

[19] library of parameterized modules (LPM) Definition. Disponível em


<http://quartushelp.altera.com/9.1/mergedProjects/reference/glossary/def_lpm.htm>.
Acesso em: 01/02/2015.

[20] Komodo Edit 8, ActiveState Software Inc. Disponível em


< http://komodoide.com/komodo-edit/>. Acesso em: 21/01/2015.

[21] MPLAB, Microchip Technology Inc.< http://www.microchip.com/pagehandler/en-


us/devtools/mplab/home.html> Acesso em: 20/07/2015.

[22] Hex2Coe. Disponível em


< https://code.google.com/p/ravr-on-xilinx-x3s500e-
platform/source/browse/trunk/sw/hex2coe/src/hex2coe.c?r=5>. Acesso em: 28/06/2015.

46
Apêndice A

Código em Verilog
//-------------------------------------------------------------------------------------------------------//
//
// UNIVERSIDADE FEDERAL DO RIO DE JANEIRO
// ESCOLA POLITÉCNICA
// DEPARTAMENTO DE ENGENHARIA ELETRÔNICA E DE COMPUTAÇÃO
//
//-------------------------------------------------------------------------------------------------------//
//
// PROJETO DE GRADUAÇÃO
//
// Projeto e Simulação de um Microcontrolador como Exemplo
// de Implementação de um Sistema Digital
//
// Aluno: Eduardo Frimer
// efrimer@poli.ufrj.br
//
// Orientador: José Paulo Brafman
//
//-------------------------------------------------------------------------------------------------------//
// Descrição
//-------------------------------------------------------------------------------------------------------//
// núcleo do PIC16F84
//-------------------------------------------------------------------------------------------------------//
module Core (progDatI, progAdrO, ramDatI, ramDatO, ramAdrO, readramO,
writeramO, existeepromI, eepDatI, eepDatO, eepAdrO,
rdEepReqO, rdEepAckI, wrEepReqO, wrEepAckI, portAI,
portAO, portADirO, portBI, portBO, portBDirO, rbpuO,
int0I, int4I, int5I, int6I, int7I, t0ckiI, wdtEnaI,
wdtClkI, wdtFullO, powerdownO, startclkO, ponRstNI,
mclrNI, clkI, clkO
);
//-------------------------------------------------------------------------------------------------------//
// A) Inicializações
//-------------------------------------------------------------------------------------------------------//
// Definição de parâmetros
parameter TAMANHO_PILHA = 8; // Tamanho da pilha do PC
parameter BITS_PILHA = 3; // Bits necessários para a pilha do PC
parameter WDT_TAMANHO = 255; // Tamanho do timer de watch dog (WDT)
parameter WDT_BITS = 8; // Bits necessários para o timer de watch dog (WDT)
parameter BITS_AUTOMATO = 3; // Tamanho do registrador do autômato finito (bits)
parameter QRESET = 3'b100; // Estado de Reset do autômato
parameter Q1 = 3'b000; // Estado Q1 do autômato

47
parameter Q2 = 3'b001; // Estado Q2 do autômato
parameter Q3 = 3'b011; // Estado Q3 do autômato
parameter Q4 = 3'b010; // Estado Q4 do autômato

// Declarações de Entradas e Saídas (E/S)


// Barramento da ROM de programa
input [13:0] progDatI; // Barramento de dados da ROM
output [12:0] progAdrO; // Barramento de endereço da ROM
// Barramento da RAM de dados
input [7:0] ramDatI; // Barramento de leitura de dados da RAM
output [7:0] ramDatO; // Barramento de escrita de dados da RAM
output [8:0] ramAdrO; // Barramento de endereço da RAM;[8:7] indica o BANK
output readramO; // Strobe de leitura da RAM (ativo em 1)
output writeramO; // Strobe de escrita da RAM (ativo em 1)
// Barramentos da EEPROM
input existeepromI; // Flag que mostra se existe EEPROM (ativo em 1)
input [7:0] eepDatI; // Barramento de leitura de dados da EEPROM
output [7:0] eepDatO; // Barramento de escrita de dados da EEPROM
output [7:0] eepAdrO; // Barramento de endereço da EEPROM
output rdEepReqO; // Requisição de leitura da EEPROM (ativo em 1)
input rdEepAckI; // Acknowledge de leitura da EEPROM (ativo em 1)
output wrEepReqO; // Requisição de escrita na EEPROM (ativo em 1)
input wrEepAckI; // Acknowledge de escrita na EEPROM (ativo em 1)
// portas (E/S)
input [4:0] portAI; // Entrada PORT-A
output [4:0] portAO; // Saída PORT-A
output [4:0] portADirO; // TRISA: Sinal de direção de PORT-A (1:entrada, 0:saida)
input [7:0] portBI; // Entrada PORT-B
output [7:0] portBO; // Saída PORT-B
output [7:0] portBDirO; // TRISB: Sinal de direção de PORT-B (1:entrada, 0:saida)
output rbpuO; // Enable de pull-up da PORT-B
// Entradas de interrupção da PORT-B
input int0I; // PORT-B(0) INT
input int4I; // PORT-B(4) INT
input int5I; // PORT-B(5) INT
input int6I; // PORT-B(6) INT
input int7I; // PORT-B(7) INT
// Controle do Timer 0 (TMR0)
input t0ckiI; // T0CKI (PORT-A(4))
// Controle do Timer Watch Dog
input wdtEnaI; // Enable do WDT (ativo em 1)
input wdtClkI; // Clock do WDT
output wdtFullO; // Indicador de WDT cheio (ativo em 1)
// Indicadores de inicio/parada do clock da CPU
output powerdownO; // SLEEP (ativo em 1)
output startclkO; // WAKEUP (ativo em 1)
// Reset da CPU
input ponRstNI; // Power-on reset (ativo em 0)
input mclrNI; // Reset normal (ativo em 0)
// Clock da CPU

48
input clkI; // Clock de entrada
output clkO; // Clock de saída (clkI/4)
// Sinais internos
// Registradores do usuário
reg [7:0] wReg; // W
reg [7:0] tmr0Reg; // TMR0
reg [12:0] pcReg; // PCH/PCL
reg [7:0] statusReg; // STATUS
reg [7:0] fsrReg; // FSR
reg [4:0] portAISyncReg; // PORTA IN
reg [4:0] portAOReg; // PORTA OUT
reg [7:0] portBISyncReg; // PORTB IN
reg [7:0] portBOReg; // PORTB OUT
reg [7:0] eepDatReg; // EEPROM DATA
reg [7:0] eepAdrReg; // EEPROM ADDRESS
reg [4:0] pclathReg; // PCLATH
reg [7:0] intconReg; // INTCON
reg [7:0] optionReg; // OPTION
reg [4:0] trisaReg; // TRISA
reg [7:0] trisbReg; // TRISB
reg [4:0] eecon1Reg; // EECON1
// Registradores internos para controle da execução das instruções
reg [13:0] instReg; // Guarda o opcode/operando
reg [7:0] aluinp1Reg; // Origem do dado (1/2)
reg [7:0] aluinp2Reg; // Origem do dado (2/2)
reg cIn; // Carry de entrada da ALU
reg [7:0] aluoutReg; // resultado dos calculos
reg execOpReg; // Se 0, execução de um ciclo impedido (desvio)
reg intstartReg; // Se 1, execução de um ciclo impedido (interrupção)
reg sleepflagReg; // Se 1, modo SLEEP
// Pilha
reg [12:0] stackReg [TAMANHO_PILHA-1:0]; // pilha
reg [BITS_PILHA-1:0] stackPntReg; // ponteiro da pilha
// Timer Watch Dog (WDT)
reg [WDT_BITS-1:0] wdtReg; // contador WDT
reg wdtFullReg; // WDT->CPU; guarda o sinal de WDT cheio até o reset da CPU
reg wdtFullNode;
wire wdtInit; // Inicializa o WDT
reg [2:0] wdtFullSyncReg; // Sincronizador da CPU para o wdtFullReg
reg wdtClrReg; // Pedido para zerar o wdtReg
reg wdtClrReqholdReg; // Guarda os pedidos para zerar o WDT
reg [1:0] wdtClrReqReg; // Sincronizador para o wdtClrReg
wire wdtClrAck; // Acknolege para o wdtClrReg
reg wdtClrAckSyncReg; // Sincronizador da CPU para o wdtClrAck
reg wdtFullClrReg; // Pedido para zerar o wdtFullReg
reg [1:0] wdtFullclrReqReg; // Sincronizador da CPU para wdtFullClrReg
// Timer 0 (TMR0)
wire psClk; // clock para o pre-escalador
reg [7:0] pscaleReg; // pre-escalador (range 0 to 255)
reg psFullReg; // clock para o TMR0, do pre-escalador

49
wire incTmrClk; // clock para o TMR0
reg incTmrHoldReg; // Guarda o pedido de increment do TMR0
reg [7:0] rateval; // Guarda configuração do pre-escalador
// Registradores/nós de interrupção
reg [4:0] intriseReg; // detecta a borda positiva da entrada da PORT-B
reg [4:0] intdownReg; // detecta a borda negativa da entrada da PORT-B
// Triggers de interrupção
wire rbint; // trigger do RB4-7
wire inte; // trigger do RB0
reg [4:0] intclrReg; // zera intriseReg e intdownReg
// Autômato
reg [BITS_AUTOMATO-1:0] stateReg;
// Outros registradores
reg writeramReg; // Strobe de escrita da RAM
reg [8:0] ramAdrReg; // Endereço da RAM
reg clkOReg; // clock de saída
// Sincronizadores
reg inteSyncReg;
reg rbintSyncReg;
reg [1:0] incTmrSyncReg;
reg rdEepSyncReg;
reg wrEepSyncReg;
reg mclrSyncReg;
reg poweronSyncReg;
// Nós intermediários do autômato
reg [7:0] ramINode; // resultado da leitura da RAM ou registradores especiais
reg [12:0] incPcNode; // valor da predição do PC
wire [7:0] maskNode; // mascara de bits para operações lógicas
reg [8:0] addNode; // resultado do somador de 8 bits
reg [4:0] addlowNode; // resultado dos 4 bits menos significativos do somador
reg aluoutZeroNode; // 1 se a saída da ALU for 0
reg writewNode; // 1 se o dado for para o registrador W
reg writeramNode; // 1 se o dado for para a RAM ou registradores especiais
reg intNode; // 1 se há uma requisição de interrupção
reg wdtRstNode; // 1 se há uma requisição de reset pelo WDT
reg resetCond; // 1 se há qualquer requisição de reset
//Conexões auxiliares
wire [8:0] ramAdrNode;
wire addrTmr0, addrPcl, addrStat, addrFsr, addrPortA, addrPortB, addrEepDat;
wire addrEepAdr, addrPclath, addrIntcon, addrOption, addrTrisa, addrTrisb;
wire addrEecon1, addrEecon2;
reg aux;
//-------------------------------------------------------------------------------------------------------//
// B) Funções e Tarefas
//-------------------------------------------------------------------------------------------------------//
// 1) Sincronizadores da CPU
always @(posedge clkI)
begin
inteSyncReg = inte;
rbintSyncReg = rbint;

50
wdtClrAckSyncReg = wdtClrAck;
mclrSyncReg = mclrNI;
poweronSyncReg = ponRstNI;
rdEepSyncReg = rdEepAckI;
wrEepSyncReg = wrEepAckI;
incTmrSyncReg[0] = incTmrClk;
incTmrSyncReg[1] = incTmrSyncReg[0];
if (~poweronSyncReg || ~mclrSyncReg)
wdtFullSyncReg = 3'b0;
else
begin
wdtFullSyncReg[0] = wdtFullReg;
wdtFullSyncReg[1] = wdtFullSyncReg[0];
wdtFullSyncReg[2] = wdtFullSyncReg[1];
end
end
// 2) Decodifica o OPcode, somente 1 sinal terá valor logico '1'
assign instCall = (instReg[13:11] == 3'b100 ); //CALL
assign instGoto = (instReg[13:11] == 3'b101 ); //GOTO
assign instBcf = (instReg[13:10] == 4'b0100 ); //BCF
assign instBsf = (instReg[13:10] == 4'b0101 ); //BSF
assign instBtfsc = (instReg[13:10] == 4'b0110 ); //BTSFC
assign instBtfss = (instReg[13:10] == 4'b0111 ); //BTFSS
assign instMovlw = (instReg[13:10] == 4'b1100 ); //MOVLW
assign instRetlw = (instReg[13:10] == 4'b1101 ); //RETLW
assign instSublw = (instReg[13:9] == 5'b11110 ); //SUBLW
assign instAddlw = (instReg[13:9] == 5'b11111 ); //ADDLW
assign instIorlw = (instReg[13:8] == 6'b111000 ); //IORLW
assign instAndlw = (instReg[13:8] == 6'b111001 ); //ANDLW
assign instXorlw = (instReg[13:8] == 6'b111010 ); //XORLW
assign instSubwf = (instReg[13:8] == 6'b000010 ); //SUBWF
assign instDecf = (instReg[13:8] == 6'b000011 ); //DECF
assign instIorwf = (instReg[13:8] == 6'b000100 ); //IORWF
assign instAndwf = (instReg[13:8] == 6'b000101 ); //ANDWF
assign instXorwf = (instReg[13:8] == 6'b000110 ); //XORWF
assign instAddwf = (instReg[13:8] == 6'b000111 ); //ADDWF
assign instMovf = (instReg[13:8] == 6'b001000 ); //MOVF
assign instComf = (instReg[13:8] == 6'b001001 ); //COMF
assign instIncf = (instReg[13:8] == 6'b001010 ); //INCF
assign instDecfsz = (instReg[13:8] == 6'b001011 ); //DECFSZ
assign instRrf = (instReg[13:8] == 6'b001100 ); //RRF
assign instRlf = (instReg[13:8] == 6'b001101 ); //RLF
assign instSwapf = (instReg[13:8] == 6'b001110 ); //SWAPF
assign instIncfsz = (instReg[13:8] == 6'b001111 ); //INCFSZ
assign instMovwf = (instReg[13:7] == 7'b0000001 ); //MOVWF
assign instClrw = (instReg[13:7] == 7'b0000010 ); //CLRW
assign instClrf = (instReg[13:7] == 7'b0000011 ); //CLRF
assign instRet = (instReg[13:0] == 14'b00000000001000); //RET
assign instRetfie = (instReg[13:0] == 14'b00000000001001); //RETFIE
assign instSleep = (instReg[13:0] == 14'b00000001100011); //SLEEP

51
assign instClrwdt = (instReg[13:0] == 14'b00000001100100); //CLRWDT
// 3) Calcula o endereço de acesso a RAM
// O endereço da RAM é BANK+FSR se "d"=0, se não, BANK+"d"
assign ramAdrNode = (instReg[6:0]==0)?
{statusReg[7],fsrReg[7:0]}:{statusReg[6:5],instReg[6:0]};
// 4) Verifica se é um acesso a RAM externa
assign addrSram = (ramAdrNode[6:0] > 7'b0001011); //0CH-7FH,8CH-FFH
// 5) Verifica se é um acesso a um registrador especial, somente 1 sinal terá valor logico
// '1'
assign addrTmr0 = (ramAdrNode[7:0] == 8'b00000001); // 01H
assign addrPcl = (ramAdrNode[6:0] == 7'b0000010); // 02H, 82H
assign addrStat = (ramAdrNode[6:0] == 7'b0000011); // 03H, 83H
assign addrFsr = (ramAdrNode[6:0] == 7'b0000100); // 04H, 84H
assign addrPortA = (ramAdrNode[7:0] == 8'b00000101); // 05H
assign addrPortB = (ramAdrNode[7:0] == 8'b00000110); // 06H
assign addrEepDat = (ramAdrNode[7:0] == 8'b00001000); // 08H
assign addrEepAdr = (ramAdrNode[7:0] == 8'b00001001); // 09H
assign addrPclath = (ramAdrNode[6:0] == 7'b0001010); // 0AH, 8AH
assign addrIntcon = (ramAdrNode[6:0] == 7'b0001011); // 0BH, 8BH
assign addrOption = (ramAdrNode[7:0] == 8'b10000001); // 81H
assign addrTrisa = (ramAdrNode[7:0] == 8'b10000101); // 85H
assign addrTrisb = (ramAdrNode[7:0] == 8'b10000110); // 86H
assign addrEecon1 = (ramAdrNode[7:0] == 8'b10001000); // 88H
assign addrEecon2 = (ramAdrNode[7:0] == 8'b10001001); // 89H
// 6) Constroi a mascara de bits para operações logicas e testes de bit
assign maskNode = 1 << instReg[9:7];

//-------------------------------------------------------------------------------------------------------//
// C) Nós intermediarios
//-------------------------------------------------------------------------------------------------------//
// 5) Determina o destino

always @(posedge clkI)


begin
assign writewNode = (intstartReg) ?0 :
(instMovwf || instBcf || instBsf || instClrf) ? 0 :
(instMovlw || instAddlw || instSublw || instAndlw
|| instIorlw || instXorlw || instRetlw || instClrw) ? 1 :
(instMovf || instSwapf || instAddwf || instSubwf
|| instAndwf || instIorwf || instXorwf || instDecf
|| instIncf || instRlf || instRrf || instDecfsz
|| instIncfsz || instComf) ? ~instReg[7] :
0;
assign writeramNode = (intstartReg) ?0 :
(instMovwf || instBcf || instBsf || instClrf) ? 1 :
(instMovlw || instAddlw || instSublw || instAndlw
|| instIorlw || instXorlw || instRetlw || instClrw) ? 0 :
(instMovf || instSwapf || instAddwf || instSubwf
|| instAndwf || instIorwf || instXorwf || instDecf
|| instIncf || instRlf || instRrf || instDecfsz

52
|| instIncfsz || instComf) ? instReg[7] :
0;
// 1) Leitura da RAM ou outras fontes de dado
assign ramINode = (addrSram) ? ramDatI : // dados da RAM externa
(addrEepDat) ? eepDatReg : // dados da EEPROM externa
(addrTmr0) ? tmr0Reg : // dados do tmr0
(addrPcl) ? pcReg[7:0] : // dados do pcl
(addrStat) ? statusReg : // dados do status
(addrFsr) ? fsrReg : // dados do fsr
(addrPortA) ? {3'b0,((~trisaReg[4:0] & portAOReg[4:0])
|| ( trisaReg[4:0] & portAISyncReg[4:0]))}: // dados da portA
(addrPortB) ? ((~trisbReg[7:0] & portBOReg[7:0])
|| ( trisbReg[7:0] & portBISyncReg[7:0])): // dados da portB
(addrEepAdr) ? eepAdrReg : // dados do eeprom
(addrPclath) ? {3'b0,pclathReg} : // pclath (5 bits)
(addrIntcon) ? intconReg : // dados do intcon
(addrOption) ? optionReg : // dados do option
(addrTrisa) ? {3'b0,trisaReg} : // trisa (5 bits)
(addrTrisb) ? trisbReg : // dados do trisb
(addrEecon1) ? {3'b0,eecon1Reg} : // eecon1 (5 bits)
8'bZ; // Alta impedância
// 2) PC + 1
incPcNode = pcReg + 1;
// 3) Somador (ALU)
// Somador completo de 8 bits com carry
{addNode,aux} = {1'b0,aluinp1Reg, 1'b1} + {1'b0,aluinp2Reg, cIn};
// 4 bits menos significativos do Somador
{addlowNode,aux} = {1'b0,aluinp1Reg[3:0],1'b1} + {1'b0,aluinp2Reg[3:0],cIn};
// 4) Testa se aluout = 0
assign aluoutZeroNode = (aluoutReg == 0)?1:0;
// 6) Requisição de interrupção
intNode = intconReg[7] // GIE
&& ((intconReg[3] && intconReg[0]) // RBIE,RBIF
|| (intconReg[4] && intconReg[1]) // INTE,INTF
|| (intconReg[5] && intconReg[2]) // T0IE,T0IF
|| (intconReg[6] && eecon1Reg[4]));// EEIE,EEIF(EECON1)
// 7) Condições de reset
assign resetCond = (~poweronSyncReg || ~mclrSyncReg ||
(wdtFullSyncReg[1] && ~wdtFullSyncReg[2]))? 1 :0;

//-------------------------------------------------------------------------------------------------------//
// D) Autômato Finito
//-------------------------------------------------------------------------------------------------------//
case (stateReg)
default : // Estados Ilegais (idealmente nunca atingidos)
stateReg = QRESET;
QRESET : // Estado de Reset
begin
pcReg = 0;
statusReg[7:5] = 3'b0;

53
pclathReg = 0;
intconReg[7:1] = 7'b0;
optionReg = -1; // 1 em todos os bits
trisaReg = -1;
trisbReg = -1;
tmr0Reg = 0; // Datasheet: don't care
execOpReg = 0;
intclrReg = -1;
intstartReg = 0;
writeramReg = 0;
sleepflagReg = 0;
// Seta /T0 and /PD
if (~poweronSyncReg) // Power-on Reset
begin
statusReg[4] = 1; // /T0 = 1
statusReg[3] = 1; // /PD = 1
stackPntReg = 0; // Reseta o ponteiro da pilha
end
else if (~mclrSyncReg) // MCLR reset/MCLR wake up do modo sleep
begin
statusReg[4] = 1; // /T0 = 1
statusReg[3] = ~sleepflagReg; // /PD = 1 se reset normal, /PD = 0 se wake up
end
else if (wdtRstNode) // WDT reset/WDT wake up do modo sleep
begin
statusReg[4] = 0; // /T0 = 0
statusReg[3] = ~sleepflagReg; // /PD = 1 se reset normal, /PD = 0 se wake up
end
// A maior parte dos bits do eecon1 são zerados
eecon1Reg[4] = 0;
eecon1Reg[2:0] = 3'b0;
if (~poweronSyncReg) eecon1Reg[3] = 0; // zera o WRERR
else eecon1Reg[3] = eecon1Reg[1]; // escreve o valor de WR em WRERR
// Vai para o estado Q1 se não a sinal de reset
if (~resetCond) stateReg = Q1;
end
Q1 : // Estado Q1
begin
// 1) Zera os registradores externos de interrupção se GIE=0
if (intconReg[7]) intclrReg = 0;
else intclrReg = 1; // Zera o registrador de interrupção
// 2) Leitura das portas (E/S)
portAISyncReg = portAI;
portBISyncReg = portBI;
// 3) Leitura/Escrita da EEPROM
if (~intstartReg)
begin
if (eecon1Reg[0] && rdEepSyncReg) // fim da leitura na EEPROM
begin
eepDatReg = eepDatI;

54
eecon1Reg[0] = 0; // Zera o EECON1_RD
end
if (eecon1Reg[1] && wrEepSyncReg) // fim da escrita na EEPROM
begin
if (intconReg[7] && intconReg[6])
eecon1Reg[4] = 1; // INT (fim da escrita na EEPROM)
eecon1Reg[1] = 0; // zera o EECON1_WR
end
if (execOpReg) ramAdrReg = ramAdrNode; // Endereço de leitura da RAM
end
// 4) Verifica se há requisição para o incremento do TMR0
if (incTmrSyncReg == 2'b01) incTmrHoldReg = 1;
// 5) Proximo estado do autômato
if (resetCond)
stateReg = QRESET;
else
// Se estiver no modo SLEEP, espera até receber um wake-up trigger
if (sleepflagReg && ~intstartReg)
begin
if (inteSyncReg || rbintSyncReg)
begin
// Se ocorrer uma interrupção do PORT-B volta à execução normal,
// Se ocorrer um reset de ou /MCLR vai para o QRESET
sleepflagReg = 0;
stateReg = Q2;
end
end
// Se não estiver no modo SLEEP ou impedido continua a execução
else stateReg = Q2;
end
Q2 : // Estado Q2
begin
// 1) Lê a RAM e especifica os operandos da ALU
if (execOpReg && ~intstartReg) // se não está impedido
begin
// 1.1) Setar os operandos da ALU: aluinp1 e aluinp2
assign aluinp1Reg = (instMovf || instSwapf || instAddwf || instSubwf
|| instAndwf || instIorwf || instXorwf || instDecf
|| instIncf || instRlf || instRrf || instBcf
|| instBsf || instBtfsc || instBtfss || instDecfsz
|| instIncfsz || instComf) ? ramINode : // RAM/Registradores Especiais
(instMovlw || instAddlw || instSublw || instAndlw
|| instIorlw || instXorlw || instRetlw) ? instReg[7:0]: // Valor imediato ("k")
(instClrf || instClrw) ?0 :
wReg; // Registrador W
assign aluinp2Reg = (instDecf || instDecfsz) ? -1 :// Decremento
(instIncf || instIncfsz) ? 1 :// Incremento
(instSublw || instSubwf) ? ~wReg + 1 :// Complemento a 2 (SUB)
(instBcf) ? ~maskNode :
// BCF: AND com a mascara invertida ("1..101..1"), só tem um 0

55
(instBtfsc || instBtfss || instBsf) ? maskNode :
// BSF: OR com o maskNode ("0..010..0")
// FSC ou FSS: AND com o maskNode, compara com 0
wReg; // Registrador W
// 1.2) Setar o pontero do registrador de pilha (POP)
if (instRet || instRetlw || instRetfie)
stackPntReg = stackPntReg - 1; // ciclos 3,2,1,0,7,6...
// 1.3) Setar o registrador ramAdr (seta o endereço de escrita da RAM)
ramAdrReg = ramAdrNode; // RAM write address
end
// 2) Muda o valor do clock de saída
clkOReg = 1;
// 3) Verifica se há requisição para o incremento do TMR0
if (incTmrSyncReg == 2'b01) incTmrHoldReg = 1;
// 4) Proximo estado do autômato
if (resetCond) stateReg = QRESET;
else stateReg = Q3;
end
Q3 : // Estado Q3
begin
// 1) Calculo e armazenamento dos resultados no registrador de saída da ULA
if (execOpReg && ~intstartReg) // se não está impedido
begin
// 1.1) Seta a saída da ALU
assign aluoutReg = (instRlf) ? {aluinp1Reg[6:0],statusReg[0]}: //Rotaciona a
esquerda
(instRrf) ? {statusReg[0],aluinp1Reg[7:1]} : // Rotaciona a direita
(instSwapf) ? {aluinp1Reg[3:0],aluinp1Reg[7:4]} : // Troca os nibbles
(instComf) ? ~aluinp1Reg : // NOT
((instAndlw || instAndwf || instBcf || instBtfsc || instBtfss))
? (aluinp1Reg & aluinp2Reg) : // AND, zerar bits/testar bits
((instBsf || instIorlw || instIorwf))
? (aluinp1Reg | aluinp2Reg) : // OR, setar bits
((instXorlw || instXorwf))
? (aluinp1Reg ^ aluinp2Reg) : // XOR
((instAddlw || instAddwf || instSublw || instSubwf
|| instDecf || instDecfsz || instIncf || instIncfsz))
? addNode[7:0] : // Adição, Subtração, Incremento, Decremento
aluinp1Reg; // Sem alterações
// 1.2) Seta os flags C e DC
if (instAddlw || instAddwf || instSublw || instSubwf)
begin
statusReg[1] = addlowNode[4]; // flag DC
statusReg[0] = addNode[8]; // flag C
end
else if (instRlf) statusReg[0] = aluinp1Reg[7]; // flag C
else if (instRrf) statusReg[0] = aluinp1Reg[0]; // flag C
// 1.3) Seta o bit que habilita a escrita na RAM
if (writeramNode && addrSram) writeramReg = 1;
else writeramReg = 0;

56
end
else writeramReg = 0; // se está impedido
// 2) Verifica se ocorreu interrupção externa e seta o flag de interrupção,
//Incrementa TMR0
if (~intstartReg && intconReg[7]) // GIE
begin
// PORT-B0 INT
if (inteSyncReg)
begin
intconReg[1] = 1; // sea o INTF
intclrReg[0] = 1;
// zera os registradores de interrupção externa (intriseReg(0) e intdownReg(0))
end
// PORT-B[4-7] INT
if (rbintSyncReg)
begin
intconReg[0] = 1; // seta o RBIF
intclrReg[4:1] = -1;
// zera os registradores de interrupção externa (intriseReg(4-1) e intdownReg(4-1))
end
end
// Incrementa o TMR0
if (incTmrHoldReg || (incTmrSyncReg == 2'b01)) // incrementa o trigger
begin
tmr0Reg = tmr0Reg + 1; // incrementa
incTmrHoldReg = 0;
// Se ~intstart, GIE, T0IE e ocorre um estouro no timer, T0IF é setado
if (~intstartReg && intconReg[7] && intconReg[5] && (tmr0Reg == -1))
intconReg[2] = 1; // seta o T0IF
end
// 3) Proximo estado do autômato
if (resetCond) stateReg = QRESET;
else stateReg = Q4;
end
Q4 : // Estado Q4
begin
// 1) Carrega a próxima instrução do programa
instReg = progDatI;
if (~execOpReg && ~intstartReg) // se está impedido
begin
pcReg = incPcNode; // incrementa o PC
execOpReg = 1;
end
else // se não está impedido
begin
// 2) Guarda o resultado dos calculos, determina o PC, os flags e se executa o
//próximo ciclo
// 2.1) Determina o valor do registrador W, se não está em um ciclo impedido
// (~intstartReg) e os destinos de W
if (writewNode) wReg = aluoutReg; // escreve no registrador W

57
// 2.2) Esreve os dados na RAM/Registradores Especiais,
// se não está em um ciclo impedido (~intstartReg)
if (writeramNode)
begin
if (addrStat)
begin
statusReg[7:5] = aluoutReg[7:5]; // escreve IRP, RP1, RP0
statusReg[1:0] = aluoutReg[1:0]; // escreve DC,C
end
if (addrFsr) fsrReg = aluoutReg; // escreve no FSR
if (addrPortA) portAOReg = aluoutReg[4:0]; // escreve na PORT-A
if (addrPortB) portBOReg = aluoutReg; // escreve na PORT-B
if (addrEepDat) eepDatReg = aluoutReg; // escreve no EEDATA
if (addrEepAdr) eepAdrReg = aluoutReg; // escreve no EEADR
if (addrPclath) pclathReg = aluoutReg[4:0]; // escreve no PCLATH
if (addrIntcon) intconReg[6:0] = aluoutReg[6:0];
// escreve no INTCON (exceto GIE)
if (addrOption) optionReg = aluoutReg; // escreve no OPTION
if (addrTrisa) trisaReg = aluoutReg[4:0]; // escreve no TRISA
if (addrTrisb) trisbReg = aluoutReg; // escreve no TRISB
if (addrTmr0) tmr0Reg = aluoutReg; // escreve no TMR0
if (addrEecon1) // escreve no EECON1
begin
eecon1Reg[4:3] = aluoutReg[4:3];
eecon1Reg[2] = aluoutReg[2] && existeepromI;
if (aluoutReg[2:0] == 3'b110) eecon1Reg[1] = 1;
// WR: só é permitido setar esse bit
if (aluoutReg[1:0] == 2'b01) eecon1Reg[0] = 1;
// RD: só é permitido setar esse bit
end
end
// 2.3) Seta/zera o flag Z, se não está em um ciclo impedido
if (~intstartReg)
begin
if (addrStat) statusReg[2] = aluoutReg[2]; // Z=1 if result == 0
else if (instAddlw || instAddwf || instAndlw || instAndwf
|| instClrf || instClrw || instComf || instDecf
|| instIncf || instMovf || instSublw || instSubwf
|| instXorlw || instXorwf)
statusReg[2] = aluoutZeroNode // Z=1 se o resultado != 0
else if (instIorlw || instIorwf)
statusReg[2] = ~aluoutZeroNode;
end
// 2.4) Determina o PC e se deve executar o proximo ciclo.
// Depois de um ciclo impedido ir para o endereço de interrupção.
if (intstartReg)
begin
pcReg = 4; // Endereço de interrupção
execOpReg = 0; // o proximo ciclo é um ciclo impedido
end

58
else if (instRet || instRetlw || instRetfie) // instrução return
begin
pcReg = stackReg[stackPntReg];
execOpReg = 0; // o proximo ciclo é um ciclo impedido
end
else if (instGoto || instCall) // instrução goto/call
begin
pcReg = {pclathReg[4:3],instReg[10:0]};
execOpReg = 0;
end
else if (((instBtfsc || instDecfsz || instIncfsz) && aluoutZeroNode)
|| (instBtfss && ~aluoutZeroNode)) // instrução bitTest
begin
pcReg = incPcNode;
execOpReg = 0;// o proximo ciclo é um ciclo impedido, se for verdadeiro
end
else if (writeramNode && addrPcl) // PCL é o destino
begin
pcReg = pclathReg[4:0] & aluoutReg;
execOpReg = 0;
end
else
begin
// verifica o recebimento de um trigger de interrupção
if (~intNode) pcReg = incPcNode; // se não, próxima instrução é normal
else pcReg = pcReg; // Se sim, o valor do PC deve ser mantido
execOpReg = 1;
end
// 2.5) Faz um push do valor atual do PC na pilha se necessário
// Instrução CALL ou fim de um ciclo impedido de interrupção
if (instCall || intstartReg)
begin
stackReg[stackPntReg] = pcReg; // escreve o valor do PC na pilha
stackPntReg = stackPntReg + 1; // incrementa o ponteiro da pilha
end
// 2.6) Seta o bit GIE no registrador intcon (intconReg(7))
if (~intstartReg)
begin
if (intNode) // recebe um trigger de interrupção
begin
intconReg[7] = 0; // Zera o GIE
intstartReg = 1; // O próximo ciclo será um ciclo impedido
end
else if (instRetfie) // Retorno de uma instrução de interrupção
begin
intconReg[7] = 1;
intstartReg = 0;
end
else if (writeramNode && addrIntcon) // destino é o GIE
begin

59
intconReg[7] = aluoutReg[7];
intstartReg = 0;
end
else intstartReg = 0;
end
else intstartReg = 0;
// 2.7) Seta/Zera os flags /PD e /TO
if (~intstartReg)
if (instClrwdt || (instSleep && (~wdtRstNode && ~intstartReg)))
// CLRWDT ou SLEEP sem nenhum trigger de interrupção
if (instSleep)
begin
sleepflagReg = 1;
statusReg[4:3] = 2'b10; // SLEEP: /T0 = 1, /PD = 0
end
else statusReg[4:3] = 2'b11; // CLRWDT: /T0 = 1, /PD = 1
end
// 3) Zera o flag de abilitação de escrita de dados da SRAM
writeramReg = 0;
// 4) Muda o clock de saída
clkOReg = 0;
// 5) Verifica se há requisição para o incremento do TMR0
if (incTmrSyncReg == 2'b01) incTmrHoldReg = 1;
// 6) Proximo estado do autômato
if (resetCond) stateReg = QRESET;
else stateReg = Q1;
end
endcase
end

//-------------------------------------------------------------------------------------------------------//
// E) TMR0 e Watch Dog
//-------------------------------------------------------------------------------------------------------//
// 1) Pre-escalador do TMR0
// seleciona o pre-escalador
assign psClk = optionReg[5]?(t0ckiI ^ optionReg[4]):clkOReg;
// Corpo do pre-escalador
always @(posedge psClk or negedge ponRstNI)
begin
if (~ponRstNI)
begin
pscaleReg = 0;
psFullReg = 0;
end
else
begin
// seleciona o valor maximo do pre-escalador em PS2-0
case (optionReg[2:0])
3'b000 : rateval = 1;
3'b001 : rateval = 3;

60
3'b010 : rateval = 7;
3'b011 : rateval = 15;
3'b100 : rateval = 31;
3'b101 : rateval = 63;
3'b110 : rateval = 127;
3'b111 : rateval = 255;
default: rateval = 1;
endcase
if (pscaleReg >= rateval)
begin
pscaleReg = 0;
psFullReg = 1;
end
else
begin
pscaleReg = pscaleReg + 1;
psFullReg = 0;
end
end
end
// 2) seleciona o trigger de incremento do TMR0
assign incTmrClk = optionReg[3]?psClk:psFullReg;
// 3) Corpo do WDT
assign wdtInit = ~ponRstNI || ~mclrNI;
always @(posedge wdtClkI or posedge wdtInit)
begin
if (wdtInit) // (async reset)
begin
wdtReg = 0;
wdtFullReg = 0;
wdtClrReqReg = 2'b0;
wdtFullclrReqReg = 2'b0;
end
else
begin
// sincronizadores
// Pedido para zerar o WDT (CLRWDT/SLEEP)
wdtClrReqReg[0] = wdtClrReg;
wdtClrReqReg[1] = wdtClrReqReg[0];
// Pedido para zerar o flag de estouro do WDT (depois do reset por WDT)
wdtFullclrReqReg[0] = wdtFullClrReg && ~sleepflagReg;
wdtFullclrReqReg[1] = wdtFullclrReqReg[0];
// Flag de estouro
if (wdtReg >= WDT_TAMANHO) wdtFullNode = 1;
else wdtFullNode = 0;
// Corpo do contador WDT
if ((wdtClrReqReg == 2'b01) || ~wdtEnaI) wdtReg = 0;
else if (wdtFullNode) wdtReg = 0;
else wdtReg = wdtReg + 1;
// Corpo do trigger de interrupção do estouro do WDT

61
if ((wdtFullclrReqReg == 2'b01) || ~wdtEnaI) wdtFullReg = 0;
else if (wdtFullNode) wdtFullReg = 1;
end
end
// 4) Controlador do WDT
assign wdtClrAck = wdtClrReqReg[1]; // Sinal de acknowledge do reset do WDT
assign wdtFullO = wdtFullReg; // trigger do flag de estouro doWDT para CPU
always @(posedge clkI)
begin
if (~poweronSyncReg || ~mclrSyncReg)
begin
wdtClrReg = 0; // zera o registrador de pedido para zerar o WDT
wdtClrReqholdReg = 0; // 1 quando há algum pedido para zerar o WDT em espera
wdtFullClrReg = 0; // zera o registrador de pedido para zerar o flag de estouro
end
else
begin
// Pedido para zerar oWDT e guarder esse pedido
if (wdtClrReg)
if (wdtClrAckSyncReg) wdtClrReg = 0; // Anula o pedido
else if (wdtClrReqholdReg
|| ((stateReg == Q4)
&& execOpReg && ~intstartReg
&& (instClrwdt || instSleep))) // recebe pedido para zerar o flag
begin
wdtClrReg = 1;
wdtClrReqholdReg = 0;
end
// zera o flag de estouro do WDT (pedido de RESET para a CPU)
if (wdtFullClrReg) wdtFullClrReg = wdtFullSyncReg[1];
end
end
//-------------------------------------------------------------------------------------------------------//
// F) Interrupções
//-------------------------------------------------------------------------------------------------------//
// Wires usados nas listas de sensibilidade (Always@())
assign intclr0 = intclrReg[0];
assign intclr1 = intclrReg[1];
assign intclr2 = intclrReg[2];
assign intclr3 = intclrReg[3];
assign intclr4 = intclrReg[4];
// INT0
always @(posedge int0I or posedge intclr0)
begin
if (intclr0) intriseReg[0] = 0;
else intriseReg[0] = 1;
end
always @(negedge int0I or posedge intclr0)
begin
if (intclr0) intdownReg[0] = 0;

62
else intdownReg[0] = 1;
end
assign rb0Int = optionReg[6]?intriseReg[0]:intdownReg[0];
// INT4
always @(posedge int4I or posedge intclr1)
begin
if (intclr1) intriseReg[1] = 0;
else intriseReg[1] = 1;
end
always @(negedge int4I or posedge intclr1)
begin
if (intclr1) intdownReg[1] = 0;
else intdownReg[1] = 1;
end
assign rb4Int = intriseReg[1] || intdownReg[1];
// INT5
always @(posedge int5I or posedge intclr2)
begin
if (intclr2) intriseReg[2] = 0;
else intriseReg[2] = 1;
end
always @(negedge int5I or posedge intclr2)
begin
if (intclr2) intdownReg[2] = 0;
else intdownReg[2] = 1;
end
assign rb5Int = intriseReg[2] || intdownReg[2];
// INT6
always @(posedge int6I or posedge intclr3)
begin
if (intclr3) intriseReg[3] = 0;
else intriseReg[3] = 1;
end
always @(negedge int6I or posedge intclr3)
begin
if (intclr3) intdownReg[3] = 0;
else intdownReg[3] = 1;
end
assign rb6Int = intriseReg[3] || intdownReg[3];
// INT7
always @(posedge int7I or posedge intclr4)
begin
if (intclr4) intriseReg[4] = 0;
else intriseReg[4] = 1;
end
always @(negedge int7I or posedge intclr4)
begin
if (intclr4) intdownReg[4] = 0;
else intdownReg[4] = 1;
end

63
assign rb7Int = intriseReg[4] || intdownReg[4];
// Decodificação dos triggers de INT
assign inte = intconReg[4] && rb0Int;
assign rbint = intconReg[3] && (rb4Int || rb5Int || rb6Int || rb7Int);

//-------------------------------------------------------------------------------------------------------//
// G) Saídas
//-------------------------------------------------------------------------------------------------------//
assign progAdrO = pcReg; // Endereço da ROM de programa
assign ramAdrO = ramAdrReg; // Endereço da RAM
assign ramDatO = aluoutReg; // Dados para escrita na RAM
assign readramO = (stateReg[1:0] == Q2[1:0]); // Habilita a leitura na RAM
assign writeramO = writeramReg; // Habilita a escrita na RAM
assign eepAdrO = eepAdrReg; // Endereço da EEPROM
assign eepDatO = eepDatReg; // Dados para escrita na EEPROM
assign rdEepReqO = eecon1Reg[0]; // Solicita a leitura na EEPROM
assign wrEepReqO = eecon1Reg[1]; // Solicita a escrita na EEPROM
assign portAO = portAOReg; // Saída PORT-A
assign portADirO = trisaReg; // TRISA
assign portBO = portBOReg; // Saída PORT-B
assign portBDirO = trisbReg; // TRISB
assign rbpuO = optionReg[7]; // RBPU: habilita o pull-up
assign clkO = clkOReg; // clock de saída (clkI/4)
assign powerdownO = sleepflagReg; // Indicador da CPU de parada do clock
assign startclkO = inte || rbint || wdtFullReg || ~mclrNI || ~ponRstNI;
// Indicador da CPU de inicio do clock
//-------------------------------------------------------------------------------------------------------//
endmodule
//-------------------------------------------------------------------------------------------------------//

64
Apêndice B

Códigos Teste em Assembly

B.1 – Código de Teste 1


; =============================================================
list p=16F84 ; para qual processador o código será gerado
radix hex ; padrão HEXADECIMAL para valores sem identificação
include <P16F84.inc>; anexa arquivo com definições do 16F84
; =============================================================
; Tabela de definições de RAM e constantes
X equ 0CH ; define variável auxiliar X na RAM 0CH (primeira posição)
W equ 0 ; facilita a referencia a w
; =============================================================
; memória de programa
org 0 ;reset
goto inicio ;endereço 000
nop ;endereço 001
nop ;endereço 002
nop ;endereço 003
goto interrupção ;endereço 004. Endereço padrão de interrupção
; =============================================================
interrupção:
retfie; retorna da interrupção
; =============================================================
inicio:
movlw B'00000000' ; W = ajuste para os bits do intcon
movwf INTCON ; INTCON = W
clrf PORTB ; inicializa PORTB
bsf STATUS, RP0 ; seleciona banco 1 para TRISB
movwf B'00000000' ; W = ajuste para os bits do TRISB
movwf TRISB ; TRISB = W; bits= 1 entrada; bits=0 saída
bcf STATUS, RP0 ; retorna ao banco 0 (padrão do Reset)
; =============================================================
principal:
movlw 02h ; W=00000010
movwf X ; X=W=00000010
incf X; incrementa o X
subwf X; W=X-W
btfss STATUS, Z ; se Z=1 pula a próxima instrução
movwf PORTB
END ; fim do programa fonte
; =============================================================

65
B.2 – Código de Teste 2
; =============================================================
list p=16F84 ; para qual processador o código será gerado
radix hex ; padrão HEXADECIMAL para valores sem identificação
include <P16F84.inc>; anexa arquivo com definições do 16F84
; =============================================================
; Tabela de definições de RAM e constantes
X equ 0CH ; define variável auxiliar X na RAM 0CH (primeira posição)
W equ 0 ; facilita a referencia a W
; =============================================================
; memória de programa
org 0 ;reset
goto inicio ;endereço 000
nop ;endereço 001
nop ;endereço 002
nop ;endereço 003
goto interrupção ;endereço 004. Endereço padrão de interrupção
; =============================================================
interrupção:
retfie; retorna da interrupção
; =============================================================
inicio:
movlw B'00000000' ; W = ajuste para os bits do intcon
movwf INTCON ; INTCON = W
clrf PORTB ; inicializa PORTB
bsf STATUS, RP0 ; seleciona banco 1 para TRISB
movwf B'00000000' ; W = ajuste para os bits do TRISB
movwf TRISB ; TRISB = W; bits= 1 entrada; bits=0 saída
bcf STATUS, RP0 ; retorna ao banco 0 (padrão do Reset)
; =============================================================
principal:
movlw FFh ; W=11111111
clrf X; zera o X
incf X; incrementa o X
andwf X; W=W.X
btfss STATUS, Z ; se Z=1 pula a próxima instrução
movwf PORTB
END ; fim do programa fonte
; =============================================================

66
B.3 – Código de Teste 3
; =============================================================
list p=16F84 ; para qual processador o código será gerado
radix hex ; padrão HEXADECIMAL para valores sem identificação
include <P16F84.inc>; anexa arquivo com definições do 16F84
; =============================================================
; Tabela de definições de RAM e constantes
W equ 0 ;
X equ 0CH ;
W2 equ 0DH ;
; =============================================================
; memória de programa
org 0 ;reset
goto inicio ;endereço 000
nop ;endereço 001
nop ;endereço 002
nop ;endereço 003
goto interrupção ;endereço 004. Endereço padrão de interrupção
; =============================================================
interrupção:
movwf W2 ; salva o W atual
comf X ; inverte X
movf X, W ; W=X
altera:
movwf PORTB ; PORTB recebe os bits complementados
movlw A5h ; W = valor a carregar no TMR0
movwf TMR0 ; TIMER 0 contado valor ajustado até o estouro.
bcf INTCON, TOIF ; reabilita interrupção TIMER 0 antes de voltar
movf W2, W ; recupera o valor de W
retfie; retorna da interrupção
; =============================================================
inicio:
movlw B'00000000' ; W = ajuste para os bits do intcon
movwf INTCON ; INTCON = W
clrf PORTA ; inicializa PORTA e PORTB
clrf PORTB
bsf STATUS, RP0 ; seleciona banco 1 para OPTION e TRIS
movlw B'11011111' ; W = ajuste para os bits do OPTION
movwf OPTION_REG ; OPTION = W
movlw B'11111111' ; W = ajuste para os bits do TRISA
movwf TRISA ; TRISA = W; bits= 1 entrada; bits=0 saída
movwf B'00000000' ; W = ajuste para os bits do TRISB
movwf TRISB ; TRISB = W; bits= 1 entrada; bits=0 saída
bcf STATUS, RP0 ; retorna ao banco 0 (padrão do Reset)
; =============================================================
principal :
movlw 55H ; W=01010101
movwf X ;
movwf PORTB ; PORTB inicializa com 01010101

67
movlw A5h ; primeira carga do registro TMR0
movwf TMR0 ;
bcf INTCON, TOIF ; retiro eventual pedido pendente
bsf INTCON, TOIE ; habilita a interrupção do TMR0
bsf INTCON, GIE ; habilita as interrupções
; =============================================================
loop:
goto loop ; fica aqui indefinidamente
; =============================================================
END ; fim do programa fonte
; =============================================================

68
B.4 – Código de Teste 4
; =============================================================
list p=16F84 ; para qual processador o código será gerado
radix hex ; padrão HEXADECIMAL para valores sem identificação
include <P16F84.inc>; anexa arquivo com definições do 16F84
; =============================================================
; Tabela de definições de RAM e constantes
X equ 0CH ; define variável auxiliar X na RAM 0CH (primeira posição)
W equ 0 ; facilita a referencia a W
; =============================================================
; memória de programa
org 0 ;reset
goto inicio ;endereço 000
nop ;endereço 001
nop ;endereço 002
nop ;endereço 003
goto interrupção ;endereço 004. Endereço padrão de interrupção
; =============================================================
interrupção:
retfie; retorna da interrupção
; =============================================================
inicio:
movlw B'00000000' ; W = ajuste para os bits do intcon
movwf INTCON ; INTCON = W
clrf PORTB ; inicializa PORTB
bsf STATUS, RP0 ; seleciona banco 1 para TRISB
movwf B'00000000' ; W = ajuste para os bits do TRISB
movwf TRISB ; TRISB = W; bits= 1 entrada; bits=0 saída
bcf STATUS, RP0 ; retorna ao banco 0 (padrão do Reset)
; =============================================================
principal:
movlw FFh ; W=11111111
movwf X ; X=W=11111111
bcf STATUS, C ; bit carry=0
repete:
rlf X ; desloca o valor em X para a direita
btfss STATUS, C ; se C=1 pula a próxima instrução
goto fim ; quando carry=0 acabou de deslocar 8 vezes
movf X, W ; W = valor em X
escreve:
movwf PORTB ; PORTB=W=X
goto repete ; se carry=1 efetua novo deslocamento
fim:
END ; fim do programa fonte
; =============================================================

69

Você também pode gostar