Disciplina de Microprocessadores Curso de Engenharia Elétrica - UEL José Alexandre de França Londrina, 22 de outubro de 2001 1 2 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Índice Geral CAPÍTULO 1 - VISÃO GERAL................................................................................................ 8 1.1 Um Pouco de História ............................................................................. 8 1.2 Componentes do Sistema.........................................................................10 1.3 Execução de Instruções..........................................................................13 1.4 Mais um Pouco Sobre a Arquitetura de von Neumann .........................................14 1.5 1.5.1. Microprocessadores e a Memória Principal .....................................................15 Ordem dos Bytes ............................................................................................................................................ 15 1.6 Controladores de Dispositivos ...................................................................16 1.7 1.7.1. Compartilhando as Informações .................................................................18 Pinagem e Encapsulamento............................................................................................................................ 18 1.8 Comentários .......................................................................................20 CAPÍTULO 2 - MICROPROGRAMAÇÃO ............................................................................. 21 2.1 Um Exemplo de Microarquitetura ...............................................................21 2.1.1. Componentes Básicos .....................................................................................................................................22 2.1.1.1. ALU e Deslocador .................................................................................................................................22 2.1.1.2. Relógio (clock)........................................................................................................................................22 2.1.1.3. Memória Principal..................................................................................................................................23 2.1.2. As Vias de Dados ............................................................................................................................................24 2.1.3. Microinstruções ..............................................................................................................................................26 2.1.4. Temporização de Microinstruções..............................................................................................................27 2.1.5. Seqüenciamento de Microinstruções .........................................................................................................29 2.2 2.2.1. 2.2.2. Um Exemplo de Macroarquitetura...............................................................30 Pilhas .................................................................................................................................................................30 O Conjunto de Macroinstruções............................................................................................................. 31 2.3 Um Exemplo de Microprograma..................................................................33 2.3.1. A Microlinguagem de Montagem .................................................................................................................33 2.3.2. Um Exemplo de Microprograma..............................................................................................................34 2.3.2.1. 3.3.2.1. Comentários sobre o Microprograma .................................................................................35 2.4 2.4.1. 2.4.2. 2.4.3. 2.4.4. 2.4.5. Mais um Pouco Sobre Endereçamento...........................................................36 Endereçamento Imediato .............................................................................................................................36 Endereçamento Direto .............................................................................................................................36 Endereçamento de Registrador..............................................................................................................36 Endereçamento Indireto..........................................................................................................................37 Indexação ....................................................................................................................................................38 1 2.5 Nossa Macroarquitetura Real ................................................................... 38 2.5.1. Conjunto de Registradores .......................................................................................................................... 38 2.5.2. Pinagem e Sinais de Barramento ............................................................................................................ 40 2.5.3. Modos de Endereçamento ....................................................................................................................... 40 2.5.3.1. Endereçamento Imediato (IMM) ...................................................................................................... 40 2.5.3.2. Endereçamento Estendido (EXT).......................................................................................................41 2.5.3.3. Endereçamento Direto (DIR)..............................................................................................................41 2.5.3.4. Endereçamento Indexado (IND)........................................................................................................41 2.5.3.5. Endereçamento Inerente (INH) ........................................................................................................41 2.5.3.6. Endereçamento Relativo (REL) ...........................................................................................................41 2.5.4. Conjunto de Instruções ........................................................................................................................... 42 2.6 Comentários ...................................................................................... 43 CAPÍTULO 3 - ENTRADA E SAÍDA......................................................................................50 3.1 E/S Programada ................................................................................. 50 3.2 Interrupção ...................................................................................... 51 3.2.1. Modo de Operação ......................................................................................................................................... 52 3.2.1.1. Vetor de Interrupção .......................................................................................................................... 52 3.2.1.2. Tempo de Latência ............................................................................................................................... 53 3.2.1.3. Prioridade ............................................................................................................................................... 54 3.2.1.4. Interrupções Mascaráveis e Não-Mascaráveis.............................................................................. 55 3.2.2. O Controlador 8259A............................................................................................................................... 56 3.2.2.1. Diagrama de Blocos .............................................................................................................................. 56 3.2.2.2. Seqüência de Interrupção .................................................................................................................. 57 3.2.2.3. Interfaceamento .................................................................................................................................. 58 3.2.2.4. Programação........................................................................................................................................... 59 3.2.3. Interrupções no 68HC11.......................................................................................................................... 59 3.2.3.1. O vetor de interrupção ....................................................................................................................... 59 3.2.3.2. Interrupção IRQ e XIRQ ................................................................................................................ 60 3.2.3.3. Interrupção do Timer Overflow (TOI) ............................................................................................61 3.2.3.4. Interrupções na placa EVB ................................................................................................................. 62 3.2.4. Interrupções no IBM PC AT ................................................................................................................... 63 3.2.4.1. O Vetor de Interrupção do IBM PC AT .......................................................................................... 63 3.2.4.2. Trabalhando com Interrupção na Linguagem C .............................................................................. 64 3.3 3.3.1. 3.3.2. 3.3.3. 3.3.4. 3.3.5. 3.3.6. E/S via DMA ..................................................................................... 67 Como o DMA funciona ................................................................................................................................... 68 Tipos e modos de transferências........................................................................................................... 68 Operação do controlador ......................................................................................................................... 69 Implementação de DMA no IBM PC e no PC/XT ................................................................................ 69 Implementação de DMA no PC AT......................................................................................................... 69 Gerenciando o controlador de DMA ...................................................................................................... 70 3.4 Interface de Comunicação Serial............................................................... 70 3.4.1. Especificações ................................................................................................................................................ 70 3.4.1.1. Características Elétricas.................................................................................................................... 70 3.4.1.2. Sinais de Controle e Handshake ........................................................................................................ 70 2 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 3.4.1.3. Características Mecânicas.................................................................................................................. 71 CAPÍTULO 4 - CONVERSORES A/D E D/A......................................................................... 72 4.1 Discretização .....................................................................................72 4.2 Teorema da Amostragem ........................................................................72 4.3 Quantização.......................................................................................75 4.4 Codificação........................................................................................77 4.5 Recuperação do Sinal Codificado ................................................................78 4.6 4.6.1. 4.6.2. 4.6.3. Critérios de Performance ........................................................................79 Resolução ..........................................................................................................................................................79 Velocidade ...................................................................................................................................................79 Linearidade..................................................................................................................................................80 4.7 4.7.1. 4.7.2. Tipos de Conversores D/A .......................................................................80 Conversor D/A à Resistores Ponderados ..................................................................................................80 Conversor D/A à Rede R-2R....................................................................................................................82 4.8 4.8.1. 4.8.2. Tipos de Conversores A/D .......................................................................83 Paralelo ou Flash .............................................................................................................................................83 Aproximações Sucessivas ........................................................................................................................84 CAPÍTULO 5 - PROJETO DE SISTEMAS MICROCONTROLADOS ................................... 86 5.1 5.1.1. 5.1.2. 5.1.3. Principais Componentes de um Sistema Microprocessado ......................................86 Memória RAM ..................................................................................................................................................86 Memória EPROM .............................................................................................................................................87 Microcontrolador 68HC11.............................................................................................................................88 5.2 Interface entre o 68HC11 com Outros Dispositivos ..........................................88 5.3 Cristal Oscilador .................................................................................94 5.4 RESET.............................................................................................95 5.5 Projetos com o 80C31. ..........................................................................96 3 Índice de Figuras Figura 1 – Uma pequena fração do ENIAC......................................................................................................................8 Figura 2 - A máquina original de von Neumann. .............................................................................................................9 Figura 3 - Diagrama de blocos de um sistema microprocessado. ............................................................................ 11 Figura 4 – Fluxo de dados de uma máquina de von Neumann típica.........................................................................15 Figura 5 – (a) Memória big endian. (b) Memória little endian. .................................................................................16 Figura 6 – Esquema de ligação de um dispositivo de E/S e um CPU, através de um controlador de dispositivo. ...................................................................................................................................................................17 Figura 7 – Pinagem lógica de um MCU bastante simples (hipotético).....................................................................19 Figura 8 – (a) encapsulamento DIP, (b) encapsulamento PLCC, (c) encapsulamento PGA, (d) encapsulamento SMD...............................................................................................................................................................................19 Figura 9 – Encaixe de um circuito integrado DIP em seu respectivo soquete. ................................................... 20 Figura 10 –(a) ALU e (b) deslocador utilizados no exemplo..................................................................................... 22 Figura 11 – (a) Relógio com 4 saídas em atraso. (b) O diagrama de temporização das saídas. ........................ 23 Figura 12 – Os registradores utilizados para acionar os barramentos de endereço e dados. ......................... 24 Figura 13 – As vias de dados da microarquitetura exemplo. ................................................................................... 25 Figura 14 – O layout e a descrição de alguns dos campos da microinstrução que controla as vias de dados da Figura 13. ............................................................................................................................................................... 27 Figura 15 – O diagrama de blocos completo de nossa microarquitetura exemplo. ............................................. 28 Figura 16 – (a) Uma pilha. (b) A mesma pilha após o armazenamento da constante 5. .......................................31 Figura 18 – Comparações entre os endereçamentos direto e indireto. (a) Endereçamento direto. (b) Endereçamento indireto. ......................................................................................................................................... 37 Figura 19 – Conjunto de registradores do 68HC11. ................................................................................................... 39 Figura 20 – Notação utilizada pela Tabela 4............................................................................................................... 42 Figura 21 – Esquema de compartilhamento de um pino de IRQ por até oito dispositivos. ............................... 53 Figura 22 – Seqüência temporal de um exemplo de múltiplas interrupções. (RSI = Rotina de Serviço de Interrupção.).............................................................................................................................................................. 55 Figura 23 – Diagrama de blocos do 8259A.................................................................................................................. 56 Figura 24 – Interface padrão do 8259A com o barramento................................................................................... 58 Figura 25 – O 8259A utilizado em cascata. ................................................................................................................ 59 Figura 26 – Registrador OPTION. .................................................................................................................................61 Figura 27 – Registrador TFLG2. .................................................................................................................................... 62 Figura 28 – Registrador TMSK2.................................................................................................................................... 62 Figura 29 - (a) representação de um discretizador, (b) representação gráfica de um discretizador e (c) sinal analógico (linha contínua) e sinal discreto (pontos grossos) . ................................................................ 73 Figura 30 – (a) sinal analógico f(t), (b) Transformada de Fourier de f(t), F(ω), (c) representação gráfica de um discretizador de Nyquist, (d) Transformada de Fourier do discretizador de Nyquist, (e) sinal discreto no tempo e em amplitude e (f) Transformada de Fourier do sinal discreto no tempo e em amplitude..................................................................................................................................................................... 74 Figura 31 – (a) sinal discretizado, f(n), (b) Transformada de Fourier de f(n), (c) gráfico da função sinc, (d) Transformada de Fourier da função sinc, (e) sinal analógico f(t) e (f) Transformada de f(t). .............. 75 Figura 32 – Diversos exemplos da Transformada de Fourier de um sinal amostrado a uma freqüência inferior a de Nyquist................................................................................................................................................ 76 Figura 33 – Esboço da função de transferência de um filtro passa-baixas real (apenas as componentes de freqüência positivas). ............................................................................................................................................... 76 Figura 34 – Quantização da temperatura em sete níveis de quantização. ........................................................... 77 Figura 35 – Processo de quantização e o erro intrínseco ao processo.................................................................. 78 4 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Figura 36 – Processo de quantização e codificação. ..................................................................................................78 Figura 37 – (a) sinal digitalizado, quantizado e codificado, (b) sinal na saída do conversor D/A e (c) sinal na saída do filtro "suavizador".....................................................................................................................................79 Figura 38 – Conversão A/D com 4 bits de resolução.................................................................................................80 Figura 39 – Linha reta aparente adquirida com um conversor A/D e o gráfico do desvio dos postos digitalizados em relação a linha reta real. ........................................................................................................... 81 Figura 40 – Característica ideal de um conversor A/D e característica de um A/D com uma linearidade diferencial pobre (com um código faltando em 120 µs). ................................................................................... 81 Figura 41 – Conversor D/A à resistores ponderados.................................................................................................82 Figura 42 – (a) Conversor R-2R de 3 bits e (b) conversor R-2R com saída amplificada....................................82 Figura 43 – Diagrama de blocos de um conversor A/D Flash. .................................................................................84 Figura 44 - Conversor A/D de 16 bits baseado em dois conversores de 8 bits..................................................84 Figura 45 – Diagrama de blocos de um conversor A/D baseado em aproximações sucessivas. .......................85 Figura 46 – Pinagem física de uma chip de memória 6264. ......................................................................................86 Figura 47 – Gravador para a família de microcontroladores da Microchip. .........................................................87 Figura 48 – Pinagem do EPROM 2764. ..........................................................................................................................88 Figura 49 –(a) Todos os 52 pinos do 68HC11, (b) Os pinos mais importantes quando deseja-se interfacea-lo com dispositivos externos, e (c) Configuração sugerida para alguns desses pinos.....................................89 Figura 50 – (a) Diagrama de tempo de um ciclo de escrita típico no 68HC11; (b) Diagrama de tempo de um ciclo de leitura típico no 68HC11............................................................................................................................90 Figura 51 – Barramento de um sistema baseado no 68HC11. O 74HC373 é utilizado para “separar” as linhas de endereço das linhas de dados............................................................................................................................90 Figura 52 – Interface entre o 68HC11 e uma memória 6264.................................................................................. 91 Figura 53 - Interface entre o 68HC11 e uma memória 6264. Neste caso, a memória possui endereço base $0000...........................................................................................................................................................................92 Figura 54 – Interface entre o 68HC11 e uma memória 6264 (base $0000) e uma memória EPROM 2764 ($E000)........................................................................................................................................................................92 Figura 55 - Interface entre o 68HC11 e uma memória 6264 (base $4000) e uma memória EPROM 2764 ($E000). Neste caso, foi utilizado o 74HC138 para atribuir os endereços base. ......................................93 Figura 56 – Mapa de memória do 68HC11. ...................................................................................................................94 Figura 57 – (a) esquema de ligação do cristal oscilador no 68HC11. (b) sugestão para confecção da placa de circuito impresso. ......................................................................................................................................................95 Figura 58 – Diversas configurações para RESET. ......................................................................................................96 Figura 59 – Pinagem do microcontrolador 80C31. ......................................................................................................96 Figura 60 – Sistema microprocessado baseado no 80C31. .......................................................................................96 Figura 61 - Sistema microprocessado baseado no 80C31 com espaços de endereçamentos diferentes para dados e instruções.....................................................................................................................................................97 5 6 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 1 - Visão Geral Índice de Tabelas Tabela 1 – Exemplos dispositivos de E/S e a maneira de como eles representam as informações................. 17 Tabela 2 – O Conjunto de instruções do Mac-1. .........................................................................................................32 Tabela 3 – Significado dos bits do registrador CCR. ................................................................................................39 Tabela 4 – Conjunto de macroinstruções do 68HC11.................................................................................................44 Tabela 5 – Descrição dos pinos do 8259A. ..................................................................................................................57 Tabela 6 - Fonte de interrupção, vetor de interrupção e a sua máscara. ............................................................60 Tabela 7 – Conteúdo do vetor de interrupção da placa EVB....................................................................................63 Tabela 8 – Canais de interrupção dos dois controladores 8259A do PC AT e suas posições no vetor de interrupção..................................................................................................................................................................64 Tabela 9 – Descrição de cada uma das posições do vetor de interrupção do PC AT..........................................67 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 7 Curso de Microprocessadores Capítulo 1 - Visão Geral 1.1 Um Pouco de História1 O estímulo para o computador eletrônico foi a Segunda Guerra Mundial. O exército americano precisava de tabelas de alcance para calibragem de mira de sua artilharia pesada, e achava que calculá-las manualmente consumia muito tempo e era sujeito a erros. John Mauchley, um desconhecido professor de física da Universidade da Pennsylvania, sabia que o exército estava interessado em calculadoras mecânicas. Da mesma maneira que muitos cientistas da computação que vieram depois dele, Mauchley apresentou um pedido de auxílio ao exército para financiamento da construção de um computador eletrônico. A proposta foi aceita em 1943, e Mauchley e seu aluno de pós-graduação, J. Presper Eckert, construíram um computador eletrônico que eles denominaram ENIAC (Electronic Numerical, Integrator And Computer, ou seja, Computador e Integrador Numérico Eletrônico). Ele era constituído de 18.000 válvulas e 1.500 relés. O ENIAC pesava 30 toneladas e consumia 140 KW de potência. Arquiteturalmente, a máquina possuía 20 registradores, cada um capaz de armazenar um número decimal de 10 dígitos. Sua programação era feita através de cerca de 6.000 chaves multiposicionais e da interconexão de um grande número de soquetes através de um verdadeiro emaranhado de cabos. Figura 1 – Uma pequena fração do ENIAC. A máquina só ficou pronta em 1946, quando já era muito tarde para ter qualquer uso em seus objetivos originais. Entretanto, logo que a guerra acabou, Mauchley e Eckert obtiveram permissão para organizar um curso de verão para descrever o trabalho deles para seus colegas cientistas. Aquele curso de verão deu início a uma explosão de interesse na construção de grandes computadores digitais. Após esse histórico curso de verão, muitos outros pesquisadores começaram a construir computadores eletrônicos. O primeiro computador operacional foi o EDSAC (1949), construído na Universidade de Cambridge, na Inglaterra, por Maurice Wilkes. Dentre outros, temos o JOHNIAC, na Rand Corporation, o ILLIAC, na University of Illinois, o MANIAC, no Los Alamos Laboratory, e o WEIZAC, no Weizmann Institute, em Israel. Eckert e Mauchley começaram a construir uma nova máquina, o EDVAC (Electronic Discrete Variable Automatic Computer, ou seja, Computador Automático Eletrônico de Variáveis Discretas), mas este projeto foi seriamente afetado quando eles deixaram a Universidade da Pennsylvania para criar uma companhia, a Eckert-Mauchley Computer Corporation, em Philadelphia (o Vale do Silício não existia ainda). Após uma série de fusões, esta companhia tornou-se a atual Unisys Corporation. 1 A maior parte desta seção foi retirada do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall. 8 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 1- Visão Geral Enquanto isso, uma das pessoas envolvidas no projeto ENIAC, John von Neumann, foi para o Instituto de Estudos Avançados de Princeton, para construir sua própria versão do EDVAC, a máquina IAS. Von Neumann era um gênio do tipo Leonardo da Vinci. Falava muitas línguas, era um especialista em ciências físicas e matemáticas, e tinha total lembrança de tudo que ouvia, via ou lia. Era capaz de citar de cor, literalmente, o texto de livros que ele havia lido anos antes. Quando ele se interessou por computadores, já era o mais eminente matemático do mundo. Uma das coisas óbvias para ele era que a programação de computadores com um grande número de chaves e cabos era lenta, tediosa e inflexível. Ele começou a perceber que o programa poderia ser representado em forma digital na memória do computador, juntamente com os dados. Observou também que a desajeitada aritmética decimal utilizada pelo ENIAC, em que cada dígito era representado por 10 válvulas (uma ligada e nove desligadas), poderia ser substituída por uma aritmética binária paralela (válvula ligada ou desligada). Seu projeto básico, agora conhecido como máquina de von Neumann, foi utilizado no EDSAC, o primeiro computador com programa armazenado, e‚ ainda é a base de quase todos os computadores digitais, até mesmo hoje, mais de meio século depois. Este projeto, e a máquina IAS, construída em colaboração com Herman Goldstine, tem tido tanta influência que vale uma breve descrição. Um esboço da arquitetura é dado na Figura 2. Figura 2 - A máquina original de von Neumann. A máquina de von Neuman possuía cinco partes básicas: a memória, a unidade lógica-aritmética, a unidade de controle de programa e os equipamentos de entrada e saída. A memória consistia de 4.096 palavras, cada palavra possuindo 40 bits (0 ou 1). Cada palavra armazenava duas instruções de 20 bits ou um inteiro de 39 bits com sinal. As instruções possuíam 8 bits dedicados a dizer o tipo da instrução, e 12 bits para especificar uma dentre 4.096 palavras de memória. Dentro da unidade lógico-aritmética, a precursora da atual CPU (Central Processing Unit, ou seja, Unidade Central de Processamento), havia um registrador interno especial de 40 bits denominado acumulador. Uma instrução típica adicionava uma palavra de memória ao acumulador ou armazenava o acumulador na memória. A máquina não possuía aritmética de ponto-flutuante, pois von Neumann achava que qualquer matemático competente deveria ser capaz de acompanhar de cabeça a posição do ponto decimal (na realidade, ponto binário). Em 1948, o transistor foi inventado no Bell Labs por John Bardeen, Walter Brattain e William Shockley, pelo qual foram agraciados com o Prêmio Nobel de Física de 1956. Nos 10 anos seguintes, o transistor revolucionou os computadores, e no final dos anos 50 os computadores a válvula estavam obsoletos. O primeiro computador transistorizado foi construído no Lincoln Laboratory do MIT, uma máquina de 16 bits baseada no Whirlwind I. Foi denominada TX-0 (Transistorized eXperimental computer 0, ou seja, computador transistorizado experimental 0), que visava meramente a ser um protótipo para testar o TX-2, uma versão melhorada. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 9 Curso de Microprocessadores O primeiro circuito integrado comercial foi inventado por Jack Kilby da Texas Instruments (http://www.ti.com) em 1958. Esta invenção foi tão significativa que Jack Kilby recebeu o maior prêmio da área tecnológica dos EUA, a National Medal of Science. O circuito integrado possibilitou que dezenas de transistores fossem colocados em uma única pastilha. Este encapsulamento tornou possível construir computadores menores, mais rápidos e mais baratos que seus predecessores transistorizados. Em 1968, a Intel Corporation (http://www.intel.com/) foi criada para fabricar pastilhas de memória. Logo depois, ela foi contactada por um fabricante de calculadoras que queria uma CPU em uma única pastilha para a sua calculadora, e por um fabricante de terminais que queria um controlador em uma única pastilha para o seu terminal. A Intel produziu ambas as pastilhas, o 4004, uma CPU de 4 bits, e o 8008, uma CPU de 8 bits. Estas foram as primeiras CPUs numa única pastilha do mundo. A lntel não esperava outros interessados além dos clientes originais, de maneira que estabeleceu uma linha de produção de baixo volume. Estavam errados. Houve um interesse tremendo, por isto começaram a projetar uma pastilha de CPU de uso geral, que resolvesse o problema do limite de 16K de memória do 8008 (imposto pelo número de pinos da pastilha). Este projeto resultou no 8080, uma pequena CPU de uso geral. Este produto tomou a indústria de assalto, e instantaneamente tornou-se um item de venda em massa. Porém, ao invés de vender milhares de unidades, como outros fabricantes, a Intel vendeu milhões. Dois anos mais tarde, em 1976, a Intel lançou o 8085, um 8080 encapsulado com alguns detalhes extras de entrada/saída. Depois surgiu o 8086, uma verdadeira CPU de 16 bits numa única pastilha. O 8086 foi projetado para ter uma certa semelhança com o 8080, mas não era completamente compatível com o 8080. O 8086 foi seguido pelo 8088, que possuía a mesma arquitetura que o 8086 e executava os mesmos programas, mas possuía um barramento de 8 bits, ao invés de um barramento de 16 bits, o que o tornava mais lento, porém mais barato que o 8086. Quando a IBM escolheu o 8088 para CPU do IBM PC original, esta pastilha tornou-se rapidamente o padrão da indústria de computadores pessoais. Nos anos seguintes, a Intel lançou o 80186 e o 80188, essencialmente novas versões do 8086 e 8088, respectivamente, mas contendo também uma grande quantidade de circuitaria de entrada/saída. Nunca foram amplamente utilizados. Com o passar dos anos, tornou-se possível colocar dezenas de milhares, depois centenas de milhares, e finalmente milhões de transistores em uma única pastilha. Este avanço tornou os microprocessadores mais rápidos e principalmente mais baratos. Deste modo, eles passaram a ser muito utilizados em sistemas baratos, para implementar soluções poderosas, para problemas simples. Isto atraiu dezenas de fabricantes a desenvolver centenas de famílias de microprocessadores, para milhares de consumidores. Cada fabricante necessitava atrair a atenção dos consumidores. Evidentemente, isto poderia ser conseguido desenvolvendo-se CPUs com características especiais que facilitassem o desenvolvimento de sistemas microprocessados. Em busca disto, vários fabricantes começaram a dotar suas CPUs com dispositivos extras. Por exemplo, uma pastilha com uma CPU e um conversor A/D2 poupa o projetista de adquirir e interfacear um A/D externo, ou seja, o sistema fica mais barato e mais simples. Com o passar dos anos, isto tornou-se comum e fez nascer uma nova classe de dispositivos: a dos microcontroladores (MCU's). 1.2 Componentes do Sistema Sozinho, um computador não faz absolutamente nada. É tão inútil quanto um guarda-chuva em dia de sol. Para fazer algo útil, precisamos programa-lo ou comprar (piratear) um programa para ele. Acredito que você tenha uma noção de como um computador funciona e de como ele é programado. Neste curso, este conhecimento será algo bastante positivo. Pois um computador nada mais é do que um sistema microprocessado bastante complexo. Desse modo, não será difícil acreditar que, assim como um computador, um sistema microprocessado geral é composto por uma CPU, memórias e dispositivos de entrada e saída (E/S). Na Seção 1.1, vimos que na maioria dos sistemas estes blocos básicos relacionam-se 2 Um conversor A/D converte uma grandeza analógica em um número digital 10 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 1- Visão Geral segundo uma arquitetura baseada no esquema da Figura 2. Esta arquitetura recebe o nome de Arquitetura de von Neumann e é ilustrada na Figura 3. Para entender a função de cada componente da Figura 3, temos que nos perguntar qual a função primária de um sistema microprocessado. Como todo sistema, um sistema microprocessado constantemente recebe dados e realiza um conjunto de operações sobre eles. Estas operações podem ser operações lógicas (deslocamentos, operações booleanas, etc) ou aritméticas (somar, multiplicar, etc). Para suportar tais instruções, um sistema microprocessado necessita de um bloco funcional que as execute. Este bloco existe e é denominado de ALU (unidade lógica e aritmética). Você já deve saber que uma ALU é um dispositivo digital que recebe dois operandos e realiza uma operação (escolhida dentre um conjunto de possíveis) entre eles. Os operandos recebidos pela ALU devem vir de algum lugar. Da mesma forma, o resultado da operação deve também ser armazenado em algum lugar. Este “lugar” é um conjunto de registradores. Ou seja, os registradores provêem operandos para a ALU e podem armazenar o resultado da operação. Agora que temos um local para armazenar nossos operandos e podemos realizar operações, necessitamos de um local para armazenar as instruções que definirão as operações realizadas pela ALU. Note que, ao contrário dos dados armazenados nos registradores, estas instruções são utilizadas durante todo o tempo no qual o sistema estiver ativo. Isto sugere a criação de um novo local para armazenar as instruções. Este novo local, geralmente com uma capacidade de armazenamento muito maior que os registradores, é chamado de memória principal. Eventualmente, podemos ter um conjunto de dados (operandos) que é utilizado durante toda a execução de um programa (como uma tabela de conversão). Além disso, podemos necessitar armazenar mais operandos que nossos registradores permitem. Por isto, a memória principal também é utilizada para armazenar dados, porém dados mais significativos. É por esta razão, que o conjunto de registradores de um sistema microprocessado é chamado de memória de rascunho. CPU Unidade de Controle Unidade Lógica e Aritmética (ALU) Dispositivos de entrada e saída Registradores Memória Principal Teclado Display de Cristal Líquido Barramento Figura 3 - Diagrama de blocos de um sistema microprocessado. Na realidade, a característica mais marcante da arquitetura de von Neumann é a forma como a CPU acessa os dados e as instruções. Nesta arquitetura, as instruções e os dados são buscados em tempos Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 11 Curso de Microprocessadores distintos. Em oposição, arquiteturas como a de Harvard possibilitam que a CPU busque as instruções e os dados ao mesmo tempo. Isto provoca um ganho substancial de performance. Contudo, provoca também um aumento significativo nos custos de produção. Por esta razão, a Arquitetura de Harvard é muito pouco utilizada. Evidentemente, em um sistema microprocessado deve haver uma fonte externa que provenha dados ao sistema. Além disso, após processar estes dados o sistema precisa apresentar um resultado ao mundo exterior. Por exemplo, em um sistema de controle de temperatura em uma estufa, precisamos alimentar o sistema de controle com dados sobre a temperatura dentro da estufa, para que este possa calcular uma ação de controle e manter a temperatura constante. Dispositivos que desempenham estas tarefas são chamados de dispositivos de entrada e saída, ou simplesmente, dispositivos de E/S. Exemplos são: conversores A/D e D/A, teclado (keypad), visores de cristal líquido, push-button, porta serial, PIO, etc. (Não se preocupe com os nomes, até o final do curso você ficará bastante íntimo deles.) Registradores, memórias, operandos, instruções, dispositivos de E/S, todos estes itens fazem parte do mesmo sistema. Logo, eles devem interagir entre si. Ou seja, durante a execução de um programa, dados e instruções vêm (e vão) de (e para) todos os componentes do sistemas. Para que isto aconteça, eles devem estar interligados por uma estrutura (caminho) comum. Este caminho é denominado barramento. Acredito que o conceito de barramento já esteja bem definido em sua mente desde o curso anterior. Contudo, quero reforçar o conceito comparando o barramento às estradas de uma cidade. Por estas estradas, veículos de todos os tipos vão (e vem) de (e para) todos os lugares de uma cidade, de uma forma bastante ordenada (pelo menos em tese), controlados por regras e sinais de controle bem determinados. É exatamente assim que se comporta um barramento em um sistema microprocessado. Neste ponto, pode parecer que todos os componentes do sistema estejam trabalhando em harmonia e em perfeito sincronismo. Contudo, para que isto realmente aconteça é necessário a inclusão de mais um componente. Este componente deve controlar todos os outros, deve sincronizar as operações e tomar decisões baseadas em seus resultados. Este bloco é denominado unidade de controle. Deve-se notar que, pelo que está escrito nos parágrafos anteriores, apenas a ALU, os registradores e a Unidade de Controle estão diretamente envolvidas no processamento dos dados. Desse modo, estes três blocos são agrupados para formar uma unidade maior e mais poderosa. É esta unidade que chamamos de Unidade Central de Processamento (CPU). Não se preocupe se a idéia de um circuito digital cuja função é determinada por um conjunto de instruções, que contenha operandos trafegando como veículos em uma cidade, e de unidade funcionais tomando decisões baseadas em acontecimentos anteriores, parecer abstrato a você. Na verdade, só quero passar a visão do todo. As partes menores ficarão bem mais claras quando forem abordadas individualmente de forma mais detalhada. A seguir, vamos agora definir formalmente a função dos blocos funcionais da Figura 3. Unidade de Lógica e Aritmética: como o próprio nome informa, realiza todas as operações lógicas e aritméticas da CPU. Em tese, a complexidade das operações da ALU define o poder de processamento da CPU, ou seja, uma CPU que contém uma ALU que execute um conjunto de operações fraco e irrelevante, torna-se uma CPU muito pouco poderosa. Registradores: são utilizados pela CPU como uma memória temporária que armazena os dados que estão sendo (ou vão ser) utilizados pela a ALU, e os resultados intermediários das operações. Memória Principal: armazena as instruções que serão executadas pela CPU e os dados mais permanentes (aqueles que serão utilizados durante toda a execução do programa ou representem dados importantes). Dispositivos de E/S: fazem a interface entre o sistema e o mundo exterior. São eles que provêem os dados utilizados pelo sistema e é através deles que os resultados são apresentados externamente. Barramento: é um meio elétrico que provê um caminho para o tráfego de informações e os sinais de controles necessários ao funcionamento do sistema. Toda a comunicação entre os componentes do sistema microprocessado é realizada através deste dispositivo. 12 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 1- Visão Geral Unidade Central de Processamento: formada pela unidade de controle, a ALU e o conjunto de registradores, é responsável pela coordenação de todas as ações do sistema microprocessado. 1.3 Execução de Instruções3 Na Seção 1.2, soubemos que, para um sistema microprocessado desempenhar uma função útil, precisamos fornecer um conjunto de instruções que devem ser executadas pela CPU. Estas instruções são armazenadas na memória principal do sistema e são analisadas pela Unidade de Controle. Contudo, para que a Unidade de Controle possa analisar a instrução, ela deve ser trazida da memória para dentro da CPU. Sabendo que a unidade de controle precisará desta instrução apenas por um intervalo de tempo muito curto, não é difícil aceitar que esta instrução fica armazenada em um registrador especial chamado registrador de instrução (IR). Além disso, a Unidade de Controle precisa saber qual será a próxima instrução a ser executada, ou seja, a UC tem que seguir a lógica de execução do programa. Para isto, existe um outro registrador especial chamado de contador de programa (PC). Na verdade, o PC não conta absolutamente nada, ele apenas aponta para a posição na memória principal, na qual localiza-se a próxima instrução a ser executada. Podemos resumir as operações realizadas pela CPU durante a execução de uma instrução em 8 passos: 1. Busca a próxima instrução da memória para o registrador de instrução. 2. Atualiza o contador de programa para que ele aponte para a instrução seguinte. 3. Determina o tipo da instrução. 4. Se a instrução usa dados da memória, determina onde eles estão. 5. Busca os dados, se houver algum, para registradores internos da CPU. 6. Executa a instrução. 7. Armazena os resultados em locais apropriados. 8. Volta ao passo 1 para iniciar a execução da próxima instrução. Esta seqüência de passos é freqüentemente referida como o “ciclo busca-decodifica-executa". Ela é o centro da operação de todos os microprocessadores. Esta descrição de como uma CPU funciona pode facilmente ser representada por um algoritmo na forma de um programa, por exemplo, em linguagem C ou assembler. O fato de ser possível escrever um programa que pode imitar a função de uma CPU mostra que um programa não precisa ser executado diretamente pelo hardware de uma CPU, constituído por um emaranhado de circuitos elétricos. Em vez disso, um programa pode ser executado tendo-se um outro programa que busca, analisa e executa suas instruções. Um programa que busca, analisa e executa as instruções de outro programa é chamado de interpretador. Esta equivalência entre processadores em hardware e interpretadores tem implicações importantes. Após ter sido especificada a linguagem de máquina L para uma nova CPU (linguagem com a qual os programadores poderão programa-la), o grupo projetista pode decidir se eles querem construir um processador (uma ALU) para executar instruções em L diretamente ou se eles querem escrever um interpretador. Se escolherem escrever um interpretador, também, precisam prover uma máquina para executá-lo. Como um interpretador quebra a instrução de sua máquina-alvo (instruções em L) em pequenos passos, a máquina na qual o interpretador é executado pode freqüentemente ser muito mais simples que o processador para a máquina-alvo (CPU que, para o programador, parece poder executar as instruções em L diretamente pelo hardware). Devido a razões econômicas, entre outras, os programas no nível de máquina convencional da maioria das CPUs modernas são executados por um interpretador em execução em uma máquina de nível 1 completamente diferente e muito mais primitiva, que chamamos nível de microprogramação. Acredito que o parágrafo anterior tenha parecido muito confuso para você. Por isso, observe um exemplo bastante simples. Uma operação de subtração A-B pode ser representada por A-B = A+1+ B . Ou Boa parte desta seção foi retirada do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 3 13 Curso de Microprocessadores seja, conseguimos emular uma operação de subtração através de duas operações de soma e uma negação. O mesmo pode ser aplicado a operações mais complexas, por exemplo, podemos simular uma multiplicação com uma seqüência de somas e deslocamentos. Qual a vantagem disto? Simular uma operação a partir de outras (geralmente mais simples), diminui a complexidade da CPU. Esta diminuição de complexidade reflete-se em um projeto mais simples (do ponto de vista do projetista da CPU), um custo menor, e até um consumo mais baixo. Contudo, não se pode querer que todas as vezes que um programador tenha de executar uma simples multiplicação, ele inclua um algoritmo para emula-la. Por isto, a CPU tem que ter este algoritmo já pronto para que ele possa ser utilizado sempre que o programador necessite. O conjunto de algoritmos internos à CPU que emulam operações complexas a partir de operações mais simples é chamado de microprograma. O microprograma é escrito a partir de um conjunto de instruções que podem ser executadas diretamente pelo hardware (através da ALU), chamadas de instruções de nível 1. Na verdade, este conjunto de instruções não é acessível ao programador. Ele é utilizado apenas pelo projetista da CPU para escrever o microprograma. Uma vez escrito, ninguém, nem mesmo o projetista, tem acesso ao microprograma. Os programadores de microprocessadores, utilizam um conjunto de instruções, geralmente chamado de linguagem assembler (ou linguagem de nível 2), que, como dito anteriormente, é emulado pelo microprograma. A coleção de todas as instruções disponíveis ao programador em um nível é chamada conjunto de instruções daquele nível. O número de instruções de um conjunto de instruções varia de máquina para máquina e de nível para nível. Para o nível de máquina convencional, por exemplo, o tamanho do conjunto de instruções está tipicamente na faixa de 20 a 300. Um conjunto de instruções numeroso não é necessariamente melhor que um pequeno. De fato, o oposto tende a ser verdade. Um conjunto de instruções numeroso muitas vezes significa que as instruções não são muito gerais. Os compiladores para linguagens de alto nível, como Ada, Modula 2 e Pascal, geralmente atuam melhor em máquinas com conjuntos de instruções pequenos e bem escolhidos do que em máquinas com conjuntos de instruções grandes e desajeitados. Máquinas com conjunto de instruções muito pequeno, chamadas máquinas RISC, serão discutidas em breve. Estas máquinas não usam microprogramação e são extremamente rápidas. Certifique-se de compreender que o conjunto de instruções e a organização do nível de microprogramação são, de fato, o conjunto de instruções e a organização do hardware (CPU). O conjunto de instruções e a organização do nível de máquina convencional são, em contraste, determinados pelo microprograma, e não pelo hardware. 1.4 Mais um Pouco Sobre a Arquitetura de von Neumann4 A organização interna de parte de uma CPU von Neumann clássica é mostrada na Figura 4 em mais detalhe. Esta parte é chamada de fluxo de dados e consiste em registradores (tipicamente 1 a 16) e da ALU (unidade lógica e aritmética). Os registradores alimentam os dois registradores de entrada da ALU, chamados de A e B na figura. Esses registradores mantêm as entradas da ALU enquanto ela está executando a operação. A própria ALU executa adição, subtração e outras operações simples com suas entradas, produzindo um resultado no seu registrador de saída. Este resultado pode ser armazenado de volta em um registrador e, de lá, de volta à memória, se desejado. O exemplo ilustra a adição. As instruções podem ser divididas em três categorias: registrador-memória, registradorregistrador e memória-memória (muito raras). Instruções registrador-memória permitem que palavras sejam buscadas da memória para registradores, onde podem ser usadas como entradas para a ALU em instruções seguintes, por exemplo. Uma instrução típica registrador-registrador busca dois operandos dos registradores, os traz para os registradores de entrada da ALU, executa alguma operação com eles e armazena o resultado de volta em um registrador. A operação do fluxo de dados é o coração da maioria 4 Todo o conteúdo desta seção foi retirada do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall. 14 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 1- Visão Geral dos sistemas. Extrapolando, ela define o que a máquina pode fazer. Voltaremos a este importante tópico em breve. Figura 4 – Fluxo de dados de uma máquina de von Neumann típica. 1.5 Microprocessadores e a Memória Principal Não vou perder meu tempo explicando como funciona um chip de memória ou falando de bits e bytes. Tenho certeza que estes conceitos estão bem claros para você. No entanto, gostaria de reforçar o conceito de nibble. No ínicio da era dos microprocessadores, surgiram várias pastilhas de 4 bits que eram usadas em calculadoras e outros aparelhos digitais. Quando refiro-me a microprocessadores de 4 bits, quero dizer que os operandos manipulados pela CPU eram de apenas de 4 bits. Em vista disto, a memória era endereçada em grupos de 4 bits chamados de nibbles. Nos dias atuais, poucos são os fabricantes, como a National Semiconductor (http://www.national.com/), que comercializam tais CPUs. Contudo, o termo nibble ainda é muito utilizado. Principalmente, porque quando se programa um microprocessador é bem mais fácil lidar com números em binário e em hexadecimal (você descobrirá isto com o tempo). Mesmo com as CPUs de 8, 16, 32 ou 64 bits, ainda podemos pensar em termo de nibble. Assim, um byte é composto de dois nibbles: o mais e o menos significativo. Além disso, como você deve saber, para converter-se um número binário em hexadecimal, pode-se operar sobre os nibbles de um byte. Por exemplo, o número 10101101b pode ser convertido para hexadecimal convertendo-se 1010b = Ah e 1101b = Eh, ou seja, 10101101b = AEh. De forma semelhante, podemos converter números em hexadecimal para binário. 1.5.1. Ordem dos Bytes Os bytes em uma palavra podem ser numerados da esquerda para a direita ou da direita para a esquerda. A princípio, parece que esta escolha não é importante, mas, como veremos brevemente, tem maiores implicações. Considere que desejamos armazenar na memória do sistema dois números de 16 bits (A037h e 1224h) e uma string de 4 bytes (“ALEX”). Duas possibilidades são apresentadas na Figura 5. Na Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 15 Curso de Microprocessadores Figura 5(a), os números e a string são armazenados da esquerda para a direita (como estamos acostumados), como na família Motorola (http://www.mot.com/). Já na Figura 5(b), a parte mais significativa de um operando de 16 bits é armazenada no endereço de memória mais alto (o que também parece lógico), porém a string continua sendo armazenada da esquerda para a direita, como na família Intel. O primeiro sistema, onde primeiro é armazenado a parte mais significativa ("big"), é chamado de sistema "big endian", em contraste com o "little endian" da Figura 5(b). Estes termos são devidos a Jonathan Swift, cujo livro “Aventuras de Gulliver” satirizou políticos que fizeram guerra por causa da disputa sobre se os ovos deviam ser quebrados do lado largo (big end) ou do lado estreito (little end). O termo foi usado pela primeira vez no encantador artigo “On Holy Wars and a Plea for Peace" de Danny Cohen (1980). Os problemas com estas duas representações começam a ocorrer quando queremos trocar dados entre duas CPUs diferentes. Por exemplo, suponha que um sistema big endian esteja conectado a um little endian por um meio que permita a transferência de um byte por vez. Para realizar a conexão, poderíamos transmitir os bytes começando no byte 0 e terminado no 3 (0, 1, 2, 3, 4, 5, 6, 7). Neste caso, as posições de memória do little endian seriam idênticas as do big endian. Com isto, a string ficaria na ordem correta, mas os números não. Caso trocássemos a ordem de envio dos dados (1, 0, 3, 2, 5, 4, 7, 6) teríamos os números corretos, mas a string não. Endereço Big endian Endereço Little endian 0 A0h 37h 0 37h A0h 2 12h 24h 2 24h 12h 4 ‘A’ ‘L' 4 ‘A’ ‘L' 6 ‘E’ ‘X’ 6 ‘E’ ‘X’ (a) (b) Figura 5 – (a) Memória big endian. (b) Memória little endian. Quero que você termine esta seção com a idéia clara de que a falta de um padrão para a ordem dos bytes é um grande problema na troca de dados entre CPUs diferentes. 1.6 Controladores de Dispositivos Como dito na Seção 1.2, a CPU precisa receber dados e enviar resultados para o “mundo exterior”. Você já sabe que isto é feito pelos dispositivos de E/S. Contudo, os tipos de dispositivos que podem ser utilizados dependem da aplicação e são infinitos. Além disso, a maneira como os diversos tipos de dispositivos representam uma informação também é infinita. A título de exemplo, são apresentados na Tabela 1 alguns dispositivos de E/S e a maneira que eles representam as informações. Evidentemente, seria muito difícil para o projetista realizar a interface entre o microprocessador e todos esses dispositivos, em vista das diferentes formas de representar a informação. Por isto, centenas de fabricantes comercializam dispositivos que realizam a interface entre a CPU e os dispositivos de E/S. Estes dispositivos são chamados de Controladores de Dispositivos. Um controlador de dispositivo faz com que a operação de E/S seja realizada (quase) como uma leitura/escrita na memória. De um lado, os controladores de dispositivos são conectados ao dispositivo de E/S da maneira adequada. Do outro, os controladores de dispositivos são conectados ao barramento do sistema. Desse modo, a CPU pode usar as linhas de endereço e os sinais de controle (WR/ RD , CS , etc) para receber e enviar as informações aos dispositivos de E/S. Na verdade, isto só é completamente correto se a CPU utilizar E/S mapeada em memória, ou seja, as mesmas instruções usadas para acessar a memória lêem/escrevem dados nos controladores de dispositivos (CD). Em outras CPUs, como as da família Intel, existem instruções próprias para trabalhar com E/S. Estas instruções podem desativar um pino de saída da CPU [algumas vezes chamado de MREQ 16 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 1- Visão Geral (REQuisição de Memória)] para avisar que trata-se de uma leitura/escrita em um CD. O sinal deste pino deve então ser incluído na lógica de seleção dos chips de memória e dos controladores de dispositivos. Tipo de Dispositivo Representação da Informação Serial Representa uma palavra binária de n bits através de uma saída (ou entrada) digital de um único pino, um bit por vez. A palavra é identificada observando-se o estado do pino em n instantes de tempo. Paralelo Representa uma palavra binária de n bits através de uma saída (ou entrada) digital de n pinos, n bits por vez. A palavra é identificada observando-se o estado dos n pinos em um único instante de tempo. Transdutores Analógicos Representam uma grandeza física qualquer, através de uma grandeza elétrica (tensão, corrente, resistência, potência, etc), geralmente na forma analógica. Teclado Representam uma tecla através da sua localização linha-coluna em uma matriz de teclas. Visor de Caracteres Representa um caractere através de um conjunto de pontos em uma matriz de pontos. Tabela 1 – Exemplos dispositivos de E/S e a maneira de como eles representam as informações. A CPU interage com um controlador de dispositivos através de um conjunto de registradores internos ao controlador. Estes registradores são divididos em três categorias: registradores de configuração, registradores de status e registradores de dados, como é ilustrado na Figura 6. É através dos registradores de configuração que a CPU diz ao controlador como quer que o dispositivo de E/S trabalhe. Ela pode definir, por exemplo, que os caracteres de um display de cristal líquido tenham uma dimensão de 5x7 pontos. Figura 6 – Esquema de ligação de um dispositivo de E/S e um CPU, através de um controlador de dispositivo. Os registradores de status são utilizados pela CPU para saber o andamento da operação de E/S ou a sua configuração. Em um dispositivo serial, por exemplo, tem-se que verificar se o dado anterior já foi enviado com sucesso antes de enviar o próximo dado. Além disso, através desses registradores a CPU pode saber se existe um dado novo em um dispositivo de entrada. É através dos registradores de dados que a CPU envia e recebe os dados de um dispositivo. Uma leitura em um desses registradores é equivalente a receber um dado do dispositivo, já uma escrita, corresponde ao envio de um dado. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 17 Curso de Microprocessadores Por último, devemos saber que quando utilizamos dispositivos de E/S, como regra geral, primeiro a CPU deve acessar os registradores de controle para configurar o dispositivo de E/S. Em seguida, caso quisermos enviar ou receber dados, a CPU precisa ler as informações contidas nos registradores de status. Só assim, ela poderá receber ou enviar os dados desejados, através do registrador de dados. Preste atenção! No parágrafo anterior, falei que sempre que desejarmos receber um dado de um dispositivo de E/S, a CPU precisa verificar o registrador de status. Agora, imagine um sistema que contenha uma infinidade de dispositivos de E/S. Neste caso, a CPU gastaria a maior parte do tempo de processamento verificando se existe dados novos em seus dispositivos. Na maior parte das vezes, a resposta seria negativa. Isto significa que a maior parte do tempo de processamento seria perdida. Para evitar esta tragédia, em alguns casos, o próprio controlador de dispositivo avisa a CPU quando existe um dado novo no dispositivo de E/S. Quando recebe este aviso, a CPU para o que está fazendo e realiza a operação de E/S. Este aviso é feito através da ativação de um sinal de controle do barramento chamado de sinal de interrupção. No Capítulo 3, estudaremos está técnica de E/S em mais detalhes. 1.7 Compartilhando as Informações Vimos até aqui a maioria dos componentes de um sistema microprocessado. Contudo, para que eles realmente façam parte de um sistema, é necessário que estejam interligados através de um caminho comum (trilhas em uma placa de circuito impresso, fios elétricos, fibra ótica, ou qualquer outra coisa que inventaram ou venham a inventar). Como você deve estar imaginando, este caminho comum é o barramento. Podem estar conectados a um barramento os pinos de saída de diversos dispositivos elétricos. Por isto, o barramento é dito um recurso compartilhado. Além disso, para que um dispositivo possa ser conectado ao barramento (sem “torrar” os pinos de saída de todos os outros), é necessário que o mesmo tenha saídas tri-state (por favor, tente lembrar-se disto). Isto também significa que, em um mesmo instante de tempo, apenas os pinos de saída de um dispositivo podem estar conectados ao barramento, ou seja, o barramento suporta apenas um mestre por vez. Chama-se mestre o dispositivo que está escrevendo dados no barramento. Já o dispositivo a que se destina os dados, chama-se escravo. Observe que esta limitação não se aplica aos escravos, isto é, podem haver mais de um escravo por vez. Você já sabe como realizar operações de Leitura/Escrita em um chip de memória. Um chip deste tipo contém pinos de endereço, dados e controle (WR/ RD , CS , etc). Estes pinos também são comuns aos outros componentes de um sistema microprocessado. Logo, os sinais que fazem parte de um barramento também podem ser divididos nestes três grupos: endereço, dados e controle. Na Seção 1.6, vimos que um controlador de dispositivo pode enviar um sinal de interrupção para a CPU. Este sinal também é transmitido pelo barramento. Isto que dizer que neste curso você verá outros sinais, além dos quais você já está acostumado (da sua experiência com chips de memória). 1.7.1. Pinagem e Encapsulamento A pinagem lógica de uma CPU geral pode parecer-se com a da Figura 7. A pinagem real pode ser diferente. Por exemplo, em 99,99% (100% é um valor muito perigoso) das CPUs, os pinos de dados são multiplexados com os de endereço. Isto acontece porque os pinos de dados e os pinos de endereço nunca são necessários em um mesmo instante (basta usarmos alguns 74373s para sustentar o endereço enquanto recebe/envia o dado). Além disso, uma CPU com menos pinos é uma CPU mais barata. Muito pode ser dito sobre um MCU observando-se apenas a sua pinagem. Principalmente com relação aos pinos de dados e endereço. Pois o poder de processamento de uma CPU é intimamente ligado com a quantidade de pinos de endereço e de dados que ela possui. Quanto maior o número de pinos de dados, maior será a velocidade da CPU, a sua complexidade e o seu custo final. Além disso, o número de bits dos registradores de uma CPU, geralmente, é igual ao número de pinos de dados. Evidentemente, quanto maior a capacidade dos registradores maior o poder de processamento (velocidade) da CPU. Em muitas aplicações, a velocidade é uma fator importantíssimo. Sistemas que trabalham com grande quantidade de dados exigem grande quantidade de memória. Desde que, para endereçar 2n bytes uma CPU necessita de n pinos de endereço, o número de linhas de 18 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 1- Visão Geral endereço também é um fator limitante. Contudo, quando trabalha com endereços uma CPU precisa realizar operações de soma, subtração, deslocamentos, etc. Por isso, aumentando-se o número de bits dos endereços com os quais a CPU trabalha, aumenta-se também a complexidade dessas operações. Por isso, uma CPU com um grande número de linhas (pinos) de endereço, geralmente, é muito poderosa e altamente complexa. Figura 7 – Pinagem lógica de um MCU bastante simples (hipotético). Apesar da multifunção de alguns pinos, algumas CPUs apresentam uma quantidade enorme de pinos. (Isto deixa os fabricantes realmente malucos na hora de encapsular o circuito integrado.) Por isso, com o passar dos anos (e o aumento dos pinos), foram surgido vários tipos de encapsulamento. Alguns, são apresentados na Figura 8. (a) (b) (c) (d) Figura 8 – (a) encapsulamento DIP, (b) encapsulamento PLCC, (c) encapsulamento PGA, (d) encapsulamento SMD. A maior parte dos pinos de uma CPU são compostos pelos pinos de endereço. As CPUs mais simples possuem no máximo 20 linhas de endereço. Essas CPUs podem facilmente ser enc1apsuladas em um encapsulamento DIP (Dual in Parallel) de 40 pinos, Figura 8(a). A CPU mais conhecida que utiliza este encapsulamento talvez seja a Intel 8088. É importante lembrar que as CPUs nem sempre são soldadas sobre a placa de circuito impresso. Solda-se primeiro um soquete na placa e encaixa-se o circuito integrado neste soquete, Figura 9. Como dito na Seção 1.1, os microcontroladores possuem diversos dispositivos de E/S encapsulados em um único chip. De fato, os microcontroladores (MCUs) são, por si só, um sistema microprocessado. Os MCUs precisam de um encapsulamento que suporte mais pinos, por isto, muitos deles utilizam o encapsulamento PLCC (Plastic Leadless Chip Carrier), um encapsulamento plástico barato que tem terminais saindo pelos quatro lados do CI, na maioria das vezes dobrado para baixo, de modo a encaixar-se Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 19 Curso de Microprocessadores perfeitamente em um soquete apropriado, Figura 8(b). Este encapsulamento torna a retirada do chip deste soquete uma tarefa muito delicada. Figura 9 – Encaixe de um circuito integrado DIP em seu respectivo soquete. Nos encapsulamento da Figura 8(a) e da Figura 8(b), cada terminal do chip recebe um número de identificação próprio. Por exemplo, chamamos "pino 20" ao vigésimo terminal contando a partir do primeiro. O terceiro tipo de padrão de pinagem que existe para circuitos integrados é o PGA (Pin Grid Array), em que os terminais saem por baixo do circuito integrado (Figura 8(c)), formando linhas e colunas que são referidas não mais por uma numeração seqüencial, mas sim por uma numeração baseada em linhas e colunas, como em uma batalha naval (por exemplo, chamamos de C3 o terminal localizado na interseção da terceira linha com a terceira coluna), pois agora a quantidade de terminais é imensa. Existe ainda um quarto tipo de invólucro, chamado PQFP (Piastic Quad Fiat Packa,ge), em que os terminais saem lateralmente pelos quatro cantos do invólucro (Figura 8(d)). Diferentemente do PLCC, que é projetado para ser encaixado em soquetes apropriados, o PQFP é projetado para ser soldado diretamente sobre a placa de circuito impresso com uma técnica especial, chamada SMD (Surface Mountage Devices - Dispositivos em Montagma de Superfície). Por isso, o encapsulamento PQFP é mais conhecido como encapsulamento SMD. A técnica SMD também está relacionada com a disposição dos terminais de um circuito integrado e permitiu a diminuição do circuito como um todo em uma placa de circuito impresso. Esta técnica consiste em soldar diretamente um circuito integrado sobre a placa de circuito impresso através de calor indireto utilizando, para isso, modernos equipamentos de automação e robótica. Isto faz com que os circuitos sejam menores por um simples motivo: são os terminais e o encapsulamento que são os responsáveis pela maior parte do tamanho de um CI. Com esta técnica, o tamanho do encapsulamento é reduzido consideravelmente, bem como os terminais. Em uma grande linha de montagem, tais circuitos são soldados automaticamente por máquinas autômatas após a confecção da placa de circuito impresso. A grande maioria dos circuitos integrados possuem versões em SMD, mesmo os circuitos integrados mais antigos em MSI e LSI. Com isto, temos aparelhos e circuitos cada vez menores – leia-se aí qualquer tipo de circuito eletrônico como aparelhos de videocassete, aparelhos de controle remoto, etc. 1.8 Comentários Pronto! Você já sabe o que é um sistema microprocessado. Mais ainda, você sabe as partes que o compõem e com elas relacionam-se. Os capítulos que seguem, simplesmente, descrevem bem mais detalhadamente este relacionamento. Além disso, nos ensina como projetar nosso próprio sistema microprocessado. Para isto, procurei fornecer a maior quantidade de informação possível. No entanto, devido a grande variedade de microprocessadores e microcontroladores existentes no mercado, tenho certeza que não será tudo (se é que “tudo” realmente existe). Como já disse, um fato está do nosso lado: a filosofia de todas as CPUs é a mesma, o que muda entre uma família e outra é a maneira de fazer a mesma coisa. Algumas dessas maneiras serão discutidas quando falarmos sobre arquiteturas avançadas de microprocessadores. 20 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação Capítulo 2 - Microprogramação5 O limite entre hardware e software não é bem definido e, além disso, está constantemente se alterando. Os primeiros computadores tinham instruções para operações aritméticas, booleanas, deslocamentos, comparações, entre outras, que eram executadas diretamente pelo hardware. Para cada instrução, um circuito especifico de hardware estava presente para executá-la. Poder-se-ia, pelo menos em princípio, desparafusar o painel traseiro e apontar os componentes eletrônicos usados pela instrução de divisão. Nas CPUs de hoje, não é mais possível isolar os circuitos de divisão, porque não existem circuitos de divisão. Todas as instruções disponíveis no nível de máquina convencional (por exemplo, instruções aritméticas, booleanas, deslocamentos, comparações e MCUs) são executadas passo a passo por um interpretador executado no nível de microprogramação. O equivalente atual de olhar para os circuitos de divisão é obter uma listagem do microprograma e procurar pela parte que interpreta as instruções de divisão. Embora programas em qualquer nível possam ser executados por um software interpretador, e embora este interpretador possa, também, ser executado por outro interpretador, essa hierarquia não pode prosseguir indefinidamente. No nível mais baixo, deve existir fisicamente uma máquina em hardware, com circuitos integrados e objetos "sólidos" similares. Neste capítulo, iremos estudar como os componentes eletrônicos são controlados pelo microprograma e como este interpreta o nível de máquina convencional. Em breve, estudaremos uma classe de máquinas que não são microprogramadas (máquinas RISC6). Como a arquitetura do nível de microprogramação, chamada de microarquitetura, é definida pelo hardware, ela é normalmente primitiva e difícil de programar. As considerações de tempo são, por exemplo, freqüentemente importantes. Na realidade, ninguém programa com microinstruções. Elas são utilizadas apenas uma única fez pelos projetistas para escrever o microprograma. Por isso, não as trate como as instruções que você está acostumado. O nível de microprogramação tem uma função específica: executar interpretadores para outras máquinas virtuais (esperançosamente mais razoáveis). Esta meta de projeto leva naturalmente a uma organização altamente otimizada no sentido de buscar, examinar e executar instruções de máquina convencional e, em alguns casos, instruções mais sofisticadas. Os problemas e compromissos envolvidos na organização e projeto deste nível serão examinados neste capítulo, através do projeto de uma CPU CISC (Complex Instruction Set Computers, ou seja, máquinas com um conjunto de instruções complexo, como são chamadas as CPUs que possuem um microprograma) bastante simples. 2.1 Um Exemplo de Microarquitetura No clássico livro “The C programming Language” (2ed., Prentice Hall, 1988), Brian Kernighan e Dennis Ritchie começam a discutir a linguagem C com o agora famoso programa “Hello, word!”: #include <stdio.h> main() { printf(“Hello, word!\n”); } 5 6 Todo o conteúdo deste capítulo foi retirado do livro Organização Estruturada de Computadores, de Tanenbaun, ed. Prentice/Hall. Reduced Instructions Set Computers, ou seja, computadores com um conjunto reduzidos de instruções. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 21 Curso de Microprocessadores Este programa foi escrito o mais simples possível, apenas para começar uma discussão sobre as características de um programa em C. Neste capítulo, começo com o projeto de uma CPU tão simples quanto possível, apenas para discutir os problemas e compromissos envolvidos na organização e projeto de uma CPU. 2.1.1. Componentes Básicos O trabalho do microprogramador é escrever um programa para controlar os registradores da máquina, seus barramentos, ALUs, memórias e outros componentes de hardware. Estes componentes foram estudados exaustivamente por você no ano que passou. Começaremos esta seção definido estes componentes para a nossa microarquitetura. 2.1.1.1. ALU e Deslocador Toda CPU precisa, de alguma maneira, realizar operações aritméticas. O circuito mais simples é apenas um somador, que pega duas entradas de n bits e produz sua soma como saída. Um circuito aritmético mais geral é a ALU. Ela possui duas entradas de dados e uma saída de dados, mas também possui algumas entradas e saídas de controle. A ALU da Figura 10(a) tem dois bits de função, F0 e F1, que determinam qual a função que a ALU deve realizar. A ALU que usaremos em nosso exemplo pode calcular A + B, A AND B, A e também A . As duas primeiras funções são auto-explicativas; a terceira simplesmente copia A para a saída; a quarta produz o inverso de A. A terceira função pode parecer inútil, mas veremos mais tarde para que ela é muito usada. Uma ALU pode também ter saídas de controle. Saídas típicas são linhas que estão em 1 quando a saída da ALU é negativa, quando é zero, quando existe um vai-1 do bit de mais alta ordem, ou quando ocorreu overflow. O exemplo da Figura 10(a) tem duas saídas de controle: N, que indica que a saída da ALU é negativa, e Z, que indica que a saída é zero. O bit N é apenas uma cópia do bit de mais alta ordem da saída. O bit Z é o NOR de todos os bits de saída da ALU. Embora algumas ALUs possam também realizar operações de deslocamento, na maioria das vezes é necessário ter uma unidade de deslocamento separada. Como você já sabe, um deslocador pode deslocar uma entrada multibit de 1 bit para a esquerda ou direita, ou, ainda, não realizar nenhum deslocamento. A Figura 10(b) é o símbolo que usaremos para um deslocador. (a) (b) Figura 10 –(a) ALU e (b) deslocador utilizados no exemplo. 2.1.1.2. Relógio (clock) Os circuitos de sistema microprocessado são normalmente acionados por um relógio, um dispositivo que emite uma seqüência periódica de pulsos. Estes pulsos definem os ciclos de máquina. Tudo que acontece na CPU (alguma atividade básica, tal como a execução de uma microinstrução), ocorre em um intervalo de tempo múltiplo de um ciclo de máquina. É freqüentemente útil dividir o ciclo de máquina em subciclos, de modo que diferentes partes da microinstrução possam ser realizadas em uma ordem bem 22 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação definida. (Lembra da Seção 1.3) Lá você viu que uma instrução é executada em diversos passos.) Por exemplo, as entradas para a ALU devem ser colocadas disponíveis e se tornarem estáveis antes que a saída possa ser armazenada. A Figura 11(a) mostra um relógio simbólico com quatro saídas. A de cima é a saída primária; as outras três são derivadas dela pela inserção de diversos atrasos nas linhas de saída. O relógio primário tem uma largura de pulso igual a um quarto do período do ciclo (Figura 11(b), linha superior). As outras três saídas são obtidas por atraso de uma, duas e três vezes a largura do pulso. O resultado é um circuito que divide cada ciclo em quatro subciclos de mesma duração. Para provocar quatro transições diferentes que devem ocorrer em um ciclo em uma certa ordem, o projetista da CPU pode fazer um AND do sinal habilitador de cada um com uma linha diferente do relógio. A transição ligada à linha de relógio primária ocorrerá primeiro, a transição ligada à linha de relógio com o menor atraso ocorrerá em segundo lugar, e assim por diante. (a) (b) Figura 11 – (a) Relógio com 4 saídas em atraso. (b) O diagrama de temporização das saídas. 2.1.1.3. Memória Principal Os processadores precisam ser capazes de ler e escrever dados na memória. A maioria das CPUs possui um barramento de endereço, um barramento de dados e um barramento de controle para a comunicação entre a CPU e a memória. Para ler da memória, a CPU coloca um endereço de memória no barramento de endereço e estabelece os sinais de controle apropriados, ativando RD (READ), por exemplo. A memória coloca, então, o conteúdo requisitado no barramento de dados. Em algumas CPUs, a leitura/escrita da memória é síncrona, isto é, a memória deve responder dentro de um tempo fixo (todas as CPUs utilizadas no curso utilizam esta técnica). Em outras, a memória pode demorar quanto quiser, sinalizando a presença de dados usando uma linha de controle quando terminar. Escritas na memória são feitas de modo similar. A CPU coloca o dado a ser escrito no barramento de dados e o endereço a ser armazenado no barramento de endereços, e então ativa WR (WRITE). (Uma alternativa para RD e WR é ter MREQ, que indica que se deseja uma requisição de memória, e RW, que distingue leitura de escrita.) Um acesso à memória é quase sempre consideravelmente mais demorado que o tempo necessário para executar uma única microinstrução. Conseqüentemente, o microprograma deve manter os valores corretos nos barramentos de dados e endereços por várias microinstruções. Para simplificar esta tarefa, é muitas vezes conveniente ter-se dois registradores, o MAR (Memory Address Register, ou registrador de endereço de memória) e o MBR (Memory Buffer Register, ou registrador de dados de memória), que alimentam os barramentos de endereços e dados, respectivamente. Para os nossos propósitos, será conveniente organizar os barramentos como indicado na Figura 12. Ambos os registradores ficam Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 23 Curso de Microprocessadores (logicamente e não fisicamente) entre a CPU e o barramento do sistema. O barramento de endereços é unidirecional em ambos os lados e é carregado do lado da CPU quando a linha de controle é ativada. A saída para as linhas de endereço do sistema está sempre habilita [ou possivelmente apenas durante as leituras e escritas, o que requer uma linha de habilitação de saída ativada pelo OR de RD e WR (não mostrado)]. A linha de controle de MBR faz com que o dado seja carregado do barramento de "Entrada de dados" no lado da CPU. A linha de "Saída de dados" está sempre habilitada. O barramento de dados do sistema é bidirecional, dando saída de MBR quando WR é ativado e carregando MBR quando RD é ativado. Figura 12 – Os registradores utilizados para acionar os barramentos de endereço e dados. 2.1.2. As Vias de Dados Agora que já vimos todos os componentes básicos com os quais se constrói o nível de microprogramação, é o momento de vermos como estes são conectados. Como, nesta área, os princípios gerais são poucos e dispersos, introduziremos o assunto através de um exemplo detalhado. As vias de dados de nossa microarquitetura exemplo estão apresentadas na Figura 13. (As vias de dados correspondem àquela parte da CPU que contém a ALU, suas entradas e suas saídas.) Elas possuem 16 registradores idênticos de 16 bits, denominados PC, AC, SP, e assim por diante, que formam uma memória de rascunho acessível apenas ao nível de microprogramação, ou seja, apenas ao microprogramador. Os registradores rotulados 0, +1 e -1 serão usados para armazenar as constantes indicadas; o significado dos nomes dos outros registradores serão explicado mais tarde. (Na realidade, 0 não foi utilizado em nossos exemplos simples, mas provavelmente o seria em uma máquina mais complicada; de qualquer forma, foi incluído porque temos mais registradores do que podemos usar.) Cada registrador pode dar saída de seu conteúdo em um ou nos dois barramentos internos, o barramento A e o barramento B, e cada um pode ser carregado a partir de um terceiro barramento interno (menos aqueles que armazenam constantes), o barramento C, como mostra a figura. Os barramentos A e B alimentam uma ALU de largura de 16 bits, que pode realizar quatro funções: A + B, A AND B, A e NOT A. A função a ser executada é especificada pelas duas linhas de controle da ALU, F0 e F1. A ALU gera dois bits de status, com base na saída corrente da ALU: N, que é ligado quando a saída da ALU é negativa, e Z, que é ligado quando a saída da ALU é zero. A saída da ALU entra em um deslocador, que pode deslocá-la de 1 bit em qualquer direção ou nenhuma. É possível executar um deslocamento de 2 bits à esquerda do conteúdo de um registrador, R, calculando R + R na ALU (que é um deslocamento de 1 bit para a esquerda) e então deslocando a soma de outro bit à esquerda utilizando o deslocador. Nem o barramento A, nem o barramento B, alimenta a ALU diretamente. Em vez disso cada um alimenta um latch (isto é, um registrador) que, por sua vez, alimenta a ALU. Os latches são necessários porque a ALU é um circuito combinatório - ela calcula continuamente a saída a partir das entradas correntes e do código de função. Esta organização pode causar problemas ao calcular, por exemplo, A = A 24 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação + B. Como A está sendo armazenado, o valor no barramento A começa a se modificar, fazendo com que a saída da ALU, e portanto também o barramento C, se modifiquem. Conseqüentemente, um valor errado pode ser armazenado em A. Em outras palavras, na atribuição A = A + B, o A do lado direito é o valor original de A, e não alguma mistura bit a bit dos valores velho e novo. Inserindo latches nos barramentos A e B, podemos congelar lá os valores originais de A e B no início do ciclo, de modo que a ALU fica protegida de mudanças nos barramentos enquanto um novo valor está sendo armazenado na memória de rascunho. A carga dos latches é controlada por L0 e L1. Figura 13 – As vias de dados da microarquitetura exemplo. Vale a pena notar que nossa solução para este problema (isto é, a inserção de latches na frente da ALU) não é a única. Se todos os registradores fossem flip-flop’s em vez de latches, então um projeto de dois barramentos seria também possível, carregando os operandos nos barramentos A e B no início do ciclo e lendo o resultado de um dos barramentos mais tarde, no ciclo. Os compromissos entre os projetos de Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 25 Curso de Microprocessadores dois e três barramentos envolvem complexidade, paralelismo e quantidade de ligações. Um tratamento mais detalhado dessas considerações está fora do escopo deste curso. Para a comunicação com a memória, incluímos um MAR e um MBR na microarquitetura. O MAR pode ser carregado a partir do latch B, em paralelo com a operação da ALU. A linha M0 controla a carga do MAR. Nas escritas, o MBR pode ser carregado com a saída do deslocador, em paralelo, ou no lugar de uma escrita na memória de rascunho. M1 controla a carga de MBR a partir da saída do deslocador. M2 e M3 controlam leituras e escritas da memória. Nas leituras, o dado lido da memória pode chegar à entrada esquerda da ALU através do multiplexador A, indicado por Amux na Figura 13. A linha de controle, A0, determina se é o latch A ou o MBR que deve alimentar a ALU. 2.1.3. Microinstruções Para controlar as vias de dados da Figura 13, precisamos de 59 sinais. Estes podem ser divididos em oito grupos funcionais, como descrito abaixo. 16 sinais para controlar a carga do barramento A a partir da memória de rascunho; 16 sinais para controlar a carga do barramento B a partir da memória de rascunho; 16 sinais para controlar a carga da memória de rascunho a partir do barramento C; 2 sinais para controlar os lanches A e B; 2 sinais para controlar a função da ALU; 2 sinais para controlar o deslocador; 4 sinais para controlar o MAR e o MBR (sendo que RD é conectado à M2 e WR é conectado à M3, como apresentado na Figura 12), e; 1 sinal para controlar o Amux. Dados os valores dos 59 sinais, podemos realizar um ciclo nas vias de dados. Um ciclo consiste em colocar valores nos barramentos A e B, armazená-los nos dois latches de barramento, passar os valores através da ALU e do deslocador e, finalmente, armazenar o resultado na memória de rascunho e/ou MBR. Além disso, o MAR pode também ser carregado, e um ciclo de memória iniciado. Como uma primeira aproximação, podemos ter um registrador de controle de 59 bits, com um bit para cada sinal de controle. Um bit em 1 significa que o sinal está ativado, e em 0 significa que ele está desativado. Entretanto, pelo preço de um pequeno aumento na circuitaria, podemos reduzir consideravelmente o número de bits necessário para controlar as vias de dados. Para começar, temos 16 bits para controlar a entrada do barramento A, o que permite 216 combinações de registradores fonte. Apenas 16 dessas combinações são permitidas - ou seja, apenas um dos 16 registradores de cada vez. Portanto, podemos codificar a informação sobre o barramento A em 4 bits e usar um decodificador para gerar os 16 sinais de controle. O mesmo se aplica ao barramento B. A situação é ligeiramente diferente para o barramento C. Em princípio, são possíveis armazenamentos simultâneos múltiplos na memória de rascunho, mas este aspecto virtualmente nunca é utilizado na prática, e a maioria das implementações não o provê. Assim, codificaremos o controle do barramento C em 4 bits. Tendo economizado 3 x 12 = 36 bits, precisamos agora de 23 bits de controle para acionar as vias de dados. L0 e L1 sempre são necessários em instantes bastante determinados, de forma que podem ser supridos pelo relógio, deixando-nos com 21 bits de controle. Um sinal adicional que não é estritamente necessário, mas que é freqüentemente útil, é um para habilitar/inibir o armazenamento do valor existente no barramento C na memória de rascunho. Em algumas situações, deseja-se meramente executar uma operação da ALU para gerar os sinais N e Z, mas não se quer armazenar o resultado. (Você consegue pensar em um caso desses?) Com este bit extra, que denominaremos ENC (ENable C, ou habilita C), podemos indicar se o barramento C deve ser armazenado (ENC = 1) ou não (ENC = 0). Agora, podemos controlar as vias de dados com um número de 22 bits. 26 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação O próximo passo no projeto da microarquitetura é inventar um formato de microinstrução contendo 22 bits. A Figura 14 mostra um formato desse tipo, com dois campos adicionais, COND e ADDR, que serão descritos em breve. A microinstrução contém 13 campos 11 dos quais estão apresentados na Figura 14. AMUX ALU SH MBR MAR RD WR ENC C B A AMUX 0 = latch A 1 = MBR - controla a entrada esquerda da ALU: 0 = latch A, 1 = MBR - função da ALU: 0 = A + B, 1 = A AND B, 2 = A, 3 = A - função do deslocador: 0 = nenhum deslocamento, 1 = à direita, 2 = à esquerda - carrega MBR a partir do deslocador: 0 = não carrega, 1 = carrega MBR - carrega MAR a partir do latch B: 0 = não carrega, 1 = carrega MAR - requisita leitura de memória: 0 = nenhuma leitura, 1 = carrega MBR a partir da memória - requisita escrita na memória: 0 = nenhuma escrita, 1 = escreve o conteúdo de MBR na memória - controla armazenamento na memória de rascunho: 0 = não armazena, 1 = armazena - seleciona registrador para armazenamento se ENC = 1: 0 = PC, 1 = AC, etc. - seleciona fonte do barramento B: 0 = PC, 1 = AC, etc. - seleciona fonte do barramento A: 0 = PC, 1 = AC, etc. COND ALU 0 = Não desvie 1= Desvie se N=1 2 = Desvie se Z = 1 3 = Desvie sempre 0=A+B 1 = A AND B 2=A 3= A SH 0 = Não desloque 1 = Desloque 1 bit à esq. 2 = Desloque 1 bit à dir. 3 = (não utilizado) MBR, MAR, RD, WR, ENC 0 = Não 1 = Sim Figura 14 – O layout e a descrição de alguns dos campos da microinstrução que controla as vias de dados da Figura 13. A ordem dos campos é completamente arbitrária. Esta ordem foi escolhida para minimizar os cruzamentos de linhas na figura subsequente. (Na realidade, este critério não é tão maluco como parece; cruzamentos de linhas em figuras geralmente correspondem a cruzamentos de fios em placas de circuito impresso ou em pastilhas, que causam problemas em projetos bidimensionais.) 2.1.4. Temporização de Microinstruções Apesar de que nossa discussão de como uma microinstrução pode controlar as vias de dados durante um ciclo esteja quase completa, negligenciamos um assunto até agora: a temporização. Um ciclo básico de ALU consiste em utilizar os latches A e B, prover tempo para que a ALU e o deslocador executem seu trabalho e para armazenar os resultados. É óbvio que esses eventos devem ocorrer nesta seqüência. Se tentarmos armazenar o valor existente no barramento C na memória de rascunho antes que os latches A e B sejam carregados, será armazenado lixo em vez de dados úteis. Para conseguir o seqüenciamento correto de eventos, vamos agora introduzir um relógio de quatro fases da Figura 11. Os eventos chave durante cada um dos subciclos são: 1. Carregar a próxima microinstrução a ser executada em um registrador chamado MIR (MicroInstruction Register), o registrador de microinstrução; 2. Colocar os conteúdos dos registradores nos barramentos A e B e armazená-los nos latches A e B; 3. Agora que as entradas estão estáveis, dar tempo para que a ALU e o deslocador produzam uma saída estável, e carregar o MAR se requisitado, e; 4. Agora que a saída do deslocador está estável, armazenar o valor existente no barramento C na memória de rascunho e carregar o MBR, se for necessário. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 27 Curso de Microprocessadores A Figura 15 é um diagrama de blocos detalhado da microarquitetura completa de nossa máquina exemplo. Pode parecer complicado inicialmente, mas vale a pena estudá-lo cuidadosamente. Ao compreender totalmente cada bloco e cada linha, estará bem encaminhado no entendimento do nível de microprogramação. O diagrama de blocos tem duas partes: as vias de dados, à esquerda, que já discutimos em detalhe, e a seção de controle, à direita, que veremos agora. Figura 15 – O diagrama de blocos completo de nossa microarquitetura exemplo. O maior e mais importante item na seção de controle da máquina é a memória de controle. Esta ROM rápida especial é onde as microinstruções são mantidas. Em nosso exemplo as microinstruções terão largura de 32 bits (Figura 14) e o espaço de endereçamento das microinstruções consistirá em 256 palavras de forma que a memória de controle ocupará um máximo de 256 x 32 = 8.192 bits. Como qualquer outra memória, a memória de controle precisa de um MAR e de um MBR. Chamaremos o MAR de MPC (MicroProgram Counter, ou contador de microprograma), porque sua única função é apontar para a próxima microinstrução a ser executada. O MBR é apenas o MIR, como mencionado acima. Note que a memória de controle e a memória principal são completamente diferentes, a primeira guarda o microprograma, e a segunda, o programa na linguagem de máquina convencional. A partir da Figura 15 fica claro que a memória de controle tenta continuamente copiar a microinstrução endereçada pelo MPC para o MIR. Entretanto, o MIR somente é carregado durante o subciclo 1, como indicado pela linha tracejada vinda do relógio para ele. Durante os outros três subciclos ele não é afetado, independente do que ocorre com MPC. Durante o subciclo 2, o MIR fica estável, e os vários campos começam a controlar as vias de dados. Em particular, A e B fazem com que os dados sejam colocados nos barramentos A e B. Os blocos decodificador A e decodificador B no diagrama de blocos provêm a decodificação de 4-para-16 de cada campo necessário para acionar as linhas OE1 (habilita saída para o barramento A) e OE2 (habilita saída 28 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação para o barramento B) nos registradores. O relógio ativa os latches A e B durante este subciclo, provendo entradas estáveis para a ALU durante o resto do ciclo. Enquanto os dados estão sendo colocados nos barramentos A e B, a unidade de incremento na seção de controle da máquina calcula MPC + 1, preparando a carga da próxima microinstrução da seqüência durante o ciclo seguinte. Sobrepondo estas duas operações, a execução das instruções pode ser acelerada. No terceiro subciclo, dá-se tempo à ALU e ao deslocador para a produção de resultados válidos. O campo AMUX da microinstrução determina a entrada esquerda da ALU; a entrada da direita é sempre o latch B. Embora a ALU seja um circuito combinatório, o tempo que ela gasta para calcular uma soma é determinado pelo tempo de propagação de vai-1, e não pelo atraso normal das portas. O tempo de propagação de vai-1 é proporcional ao número de bits da palavra. Enquanto a ALU e o deslocador estão calculando, o MAR é carregado a partir do barramento B, se o campo MAR da microinstrução for 1 (um). Durante o quarto e último subciclo, o valor existente no barramento C pode ser armazenado de volta na memória de rascunho e no MBR, dependendo de ENC e MBR. O bloco rotulado "decodificador C" pega ENC, a quarta linha do relógio, e o campo C da microinstrução como entradas e gera os 16 sinais de controle. Internamente, ele realiza uma decodificação de 4-para-16 do campo C e então faz um AND de cada um destes sinais com um sinal derivado do AND da linha do subciclo 4 com ENC. Assim, um registrador da memória de rascunho somente é carregado se prevalecerem três condições: 1. ENC = 1; 2. É o subciclo 4, e; 3. registrador foi selecionado pelo campo C. O MBR também é carregado durante o subciclo 4 se MBR = 1, os dois sinais que controlam a memória, RD e WR, são ativados se estiverem presentes em MIR. Na verdade, os campos correspondentes em MIR agem como latches. 2.1.5. Seqüenciamento de Microinstruções A única consideração pendente é como a próxima microinstrução é escolhida. Embora algum tempo seja suficiente para buscar a próxima microinstrução da seqüência, algum mecanismo é necessário para permitir desvios condicionais no microprograma, para possibilitar que ele tome decisões. Por esta razão, deixamos dois campos em cada microinstrução: ADDR, que é o endereço da sucessora potencial da microinstrução corrente, e COND, que determina (juntamente com as saídas N e Z da ALU) se a próxima microinstrução será buscada de MPC + 1 ou de ADDR. Toda microinstrução contém potencialmente um desvio condicional. Esta decisão foi tomada porque desvios condicionais são muito comuns em microprogramas, e, ao permitir que toda microinstrução tenha duas sucessoras possíveis, tornamo-las de execução mais rápida do que a alternativa de estabelecer alguma condição em uma microinstrução e depois testa-la na próxima. A maioria das microarquiteturas existentes usam nossa estratégia de uma forma ou outra. A escolha da próxima microinstrução é determinada pelo bloco rotulado "Lógica de Microsseqüenciamento" durante o subciclo 4, quando os sinais N e Z da saída da ALU são válidos. A saída deste bloco (não mostrada na figura) controla o multiplexador (Mmux), que roteia MPC + 1 ou ADDR para MPC, de onde será buscada a próxima microinstrução. Fornecemos ao microprogramador quatro escolhas quanto à seleção da próxima microinstrução. A escolha é indicada fazendo o campo COND igual a: 0 = Não desvie; a próxima microinstrução será tomada de MPC + 1 1 = Desvie para ADDR se N = 1 2 = Desvie para ADDR se Z = 1 3 = Desvie para ADDR incondicionalmente A lógica de microsseqüenciamento combina os dois bits da ALU, N e Z, e os dois bits de COND, L (Left) e R (Right), isto é, esquerda e direita, para gerar uma saída, O sinal correto é Mmux = LRN + LRZ + LR = RN + LZ + LR Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 29 Curso de Microprocessadores Em palavras, o sinal de controle para Mmux é 1 (roteando ADDR para MPC) se LR é 01b e N = 1, ou LR é 10b e Z = 1, ou LR é 11b. Caso contrário, ele é 0, e a próxima microinstrução na seqüência é buscada. O circuito para a geração do sinal Mmux pode ser construído a partir de componentes SSI ou ser parte de uma PLA. Para tornar nossa máquina exemplo ligeiramente mais real, vamos assumir que um ciclo de memória principal tome mais tempo que uma microinstrução. Em particular, se uma microinstrução inicia uma leitura à memória principal, fazendo RD = 1, ela também deve ter RD = 1 na próxima microinstrução executada (que pode ou não estar localizada no próximo endereço da memória de controle). O dado torna-se disponível duas microinstruções após a leitura ter sido iniciada. Se o microprograma não tem nada mais útil a fazer na microinstrução seguinte à que iniciou a leitura de memória, a microinstrução simplesmente tem RD = 1 e é efetivamente desperdiçada. Do mesmo modo, uma escrita na memória também gasta dois tempos de microinstrução para ser completada. 2.2 Um Exemplo de Macroarquitetura Para Continuar nosso exemplo do nível de microprogramação, vamos agora nos voltar para a arquitetura do nível de máquina convencional a ser suportada pelo interpretador em execução na máquina da Figura 15. Por conveniência, vamos chamar a arquitetura da máquina de nível 2 ou 3 de macroarquitetura, em contraste com o nível 1, a microarquitetura. [Para os propósitos deste texto, vamos ignorar o nível 3 (nível de sistema operacional), porque suas instruções são praticamente aquelas do nível 2, e as diferenças não são importantes aqui.] Similarmente, as instruções de nível 2 serão chamadas macroinstruções. Assim, as instruções normais de ADD, MOVE e outras do nível de máquina convencional serão chamadas macroinstruções. Vamos algumas vezes nos referenciar à nossa máquina exemplo de nível 1 como Mic-1, e à máquina de nível 2 como Mac-1. Antes de descrever a Mac-1, entretanto, vamos digressionar ligeiramente para motivar seu projeto. 2.2.1. Pilhas Uma das questões mais importantes de projeto de macroarquiteturas é o endereçamento. Um programa qualquer é normalmente implementado de tal modo que, quando se sai de um procedimento ou função, a área de memória que estava sendo utilizada para variáveis locais é liberada. A maneira mais fácil para se conseguir isto é através de uma estrutura de dados denominada pilha. Uma pilha é um bloco contíguo de memória contendo alguns dados e um apontador de pilha (SP, de Stack Pointer) dizendo onde está o topo da pilha. O fundo da pilha está em um endereço fixo, e não mais nos interessará. A Figura 16(a) mostra uma pilha ocupando seis palavras de memória. O fundo da pilha está no endereço 4020, e o topo da pilha, para onde SP aponta, está no 4015. Nossas pilhas crescerão dos endereços de memória mais altos para os mais baixos, mas a escolha oposta é igualmente boa. Muitas operações são definidas para pilhas. Duas das mais importantes são PUSH X e POP Y. PUSH avança o apontador da pilha (decrementando-o, no nosso exemplo) e então armazena X na posição de memória agora apontada por SP. PUSH aumenta o tamanho da pilha de um item. POP Y, em contraste, reduz o tamanho da pilha, armazenando o item do topo da pilha em Y, e então removendo-o através do incremento do apontador de pilha com o tamanho do item retirado. A Figura 16(b) mostra como a pilha da Figura 16(a) fica depois que uma palavra contendo 5 foi empilhada. 30 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação (a) (b) Figura 16 – (a) Uma pilha. (b) A mesma pilha após o armazenamento da constante 5. Outra operação que pode ser realizada numa pilha é avançar o apontador da pilha sem realmente inserir qualquer dado. Isto é normalmente feito na entrada de um procedimento ou função, para reservar espaço para as variáveis locais [depois que o espaço for reservado, pode-se utilizar os modos de endereçamento (você verá em breve) para armazenar itens]. É importante observar que apenas as instruções PUSH e POP podem armazenar e retirar itens da pilha. Por isso, não pense que você poderá armazenar um item na pilha (utilizando a instrução MOVE, por exemplo) e avançar o ponteiro da pilha (SP) “manualmente” (isto seria muito perigoso). Você viu que o melhor local para armazenar dados temporários (fora da CPU) é a pilha. Contudo, a pilha é uma estrutura dinâmica, quer dizer, seu topo está constantemente mudando de endereço (SP muda de conteúdo). Neste caso, todas as vezes que um procedimento ou função for chamado, o topo da pilha pode estar em um local diferente. Isto quer dizer que um item que está armazenado na pilha só pode ser referenciado em relação ao SP. Em outras palavras, o Mac-1 precisa de um modo de endereçamento que busque ou armazene uma palavra a uma distância conhecida relativa ao apontador de pilha (ou algum modo de endereçamento equivalente). Caso você não tenha entendido esta seção, pode fazer uma nova tentativa lendo o livro “Organização Estruturada de Computadores” de Tanenbaum, páginas 142 e 143. Lá, você encontrará um exemplo mais detalhado do uso de pilhas. Por enquanto, seguiremos com o nosso projeto. 2.2.2. O Conjunto de Macroinstruções Com esse modo de endereçamento em mente, estamos agora prontos para analisar a arquitetura do Mac-1. Basicamente, ela consiste em uma memória com 4.096 palavras de 16 bits e três registradores visíveis ao programador de nível 2. Os registradores são o contador de programa, PC, a apontador de pilha, SP, e o acumulador, AC, que é usado para movimentação de dados, cálculos e outros propósitos. São providos três modos de endereçamento: direto, indireto e local. As instruções que usam o endereçamento direto contêm um endereço absoluto de memória de 12 bits nos 12 bits de baixa ordem. Tais instruções são úteis para acessar variáveis globais. O endereçamento indireto permite que o programador calcule um endereço de memória, coloque-o em AC e então leia ou escreva a palavra endereçada. Esta forma de endereçamento é muito genérica e é usada para acessar elementos em arranjos (vetores), entre outras Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 31 Curso de Microprocessadores estruturas. O endereçamento local especifica uma distância a partir de SP e é usado para acessar variáveis locais, como acabamos de ver. Juntos, estes três modos provêem um sistema de endereçamento simples, mas adequado. O conjunto de instruções do Mac-1 é apresentado na Tabela 2. Cada instrução contém um código de operação (opcode) e, algumas vezes, um endereço de memória ou uma constante. A primeira coluna mostra o código binário da instrução. A segunda mostra seu mnemônico para a linguagem de montagem. A terceira mostra seu nome, e a quarta descreve o que ela faz, através de um trecho em Pascal. Nestes trechos, m[x] se refere à palavra de memória x. Assim, LODD carrega o acumulador com a palavra de memória especificada em seus 12 bits de baixa ordem. LODD usa então endereçamento direto, enquanto LODL carrega o acumulador com uma palavra a uma distância x acima de SP, usando portanto o endereçamento local. LODD, STOD, ADDD e SUBD realizam quatro funções básicas usando endereçamento direto, e LODL, STOL, ADDL e SUBL realizam as mesmas funções usando endereçamento local. Binário Mnemônico Instrução Significado 0000xxxxxxxxxxxx LODD Carrega direto Ac := m[x] 0001xxxxxxxxxxxx STOD Armazena direto M[x] := ac 0010xxxxxxxxxxxx ADDD Adiciona direto Ac := ac + m[x] 0011xxxxxxxxxxxx SUBD Subtrai direto Ac := ac - m[x] 0100xxxxxxxxxxxx JPOS Desvia se positivo If ac ≥ o then pc := x 0101xxxxxxxxxxxx JZER Desvia se zero If ac = 0 then pc := x 0110xxxxxxxxxxxx JUMP Desvia sempre Pc := x 0111xxxxxxxxxxxx LOCO Carrega constante Ac := x (0 ≤ x ≤ 4095) 1000xxxxxxxxxxxx LODL Carga local Ac := m[sp + x] 1001xxxxxxxxxxxx STODL Armazena local M[x + sp] := ac 1010xxxxxxxxxxxx ADDL Adiciona local Ac := ac + m[sp + x] 1011xxxxxxxxxxxx SUBL Subtrai local Ac := ac - m[sp + x] 1100xxxxxxxxxxxx JNEG Desvia se negativo If ac < 0 then pc := x 1101xxxxxxxxxxxx JNZE Desvia se não zero If ac ≠ 0 then pc := x 1110xxxxxxxxxxxx CALL Chama procedimento Sp := sp – 1; m[sp] := pc; pc := x 1111000000000000 PSHI Empilha indireto Sp := sp – 1; m[sp] := m[ac] 1111001000000000 POPI Desempilha indireto M[ac] := m[sp]; sp := ac 1111010000000000 PUSH Coloca na pilha Sp := sp – 1; m[sp] := ac 1111011000000000 POP Retira da pilha Ac := m[sp]; sp := sp + 1 1111100000000000 RETN Retorna Pc := m[sp]; sp := sp + 1 1111101000000000 SWAP Troca ac, sp tmp := ac; ac := sp; sp := tmp 11111100yyyyyyyy INSP Incrementa sp Sp := sp + y (0 ≤ y ≤ 255) 11111110yyyyyyyy DESP Decrementa sp Sp := sp – y (0 ≤ y ≤ 255) xxxxxxxxxxxx é um endereço de máquina de 12 bits; na coluna 4, ele é chamado de x. yyyyyyyy é uma constante de 8 bits; na coluna 4, ela é chamada de y. Tabela 2 – O Conjunto de instruções do Mac-1. Cinco instruções de desvio são fornecidas, um desvio incondicional (JUMP) e quatro condicionais (JPOS, JZER, JNEG e JNZE). JUMP sempre copia seus 12 bits de baixa ordem para o contador de programa, enquanto os outros quatro somente o fazem se a condição especificada se verificar. LOCO carrega uma constante de 12 bits, na faixa de 0 a 4095 (inclusive), em AC. A operação inversa é POPI, que retira uma palavra da pilha e a armazena na posição de memória endereçada por AC. PUSH e POP são úteis na manipulação da pilha de várias maneiras. SWAP troca o conteúdo de AC com SP, o que é útil quando SP precisa ser acrescido ou decrescido de uma quantidade não conhecida no tempo de compilação. Ela é também útil para inicializar SP no início da execução. INSP e DESP são usados para 32 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação alterar SP de valores conhecidos em tempo de compilação. Devido à falta de espaço de codificação, as distâncias aqui foram limitadas a 8 bits. Finalmente, CALL chama um procedimento, salvando o endereço de retorno na pilha, e RETN retorna de um procedimento, desempilhando o endereço de retorno e colocando-o em PC. Até agora, nossa máquina não tem instruções de entrada/saída. Nem estamos a fim de acrescentar alguma agora. Ela não precisa delas. Em vez disso, a máquina usará E/S mapeada em memória (veremos em breve). 2.3 Um Exemplo de Microprograma Tendo especificado em detalhes tanto a microarquitetura quanto a macroarquitetura, o problema restante é a implementação: com que se parece um programa sendo executado na primeira e interpretando a segunda, e como ele funciona? Antes que possamos responder a essas questões, devemos considerar cuidadosamente em que linguagem queremos fazer nosso microprograma. 2.3.1. A Microlinguagem de Montagem Em princípio, poderíamos escrever microprogramas em binário, 32 bits por microinstrução. Programadores masoquistas poderiam até gostar disso; certamente, ninguém mais iria. Sendo assim, precisamos de uma linguagem simbólica na qual possamos expressar microprogramas. Uma notação possível é fazer com que o microprogramador especifique uma microinstrução por linha, identificando cada campo não-nulo e seu valor. Por exemplo, para somar AC e A, e armazenar o resultado em AC, poderíamos escrever ENC=1,C=1,B=1,A=10. Muitas linguagens de microprogramação se parecem com isto. Entretanto, esta notação é terrível. Uma idéia muito melhor é usar uma notação de linguagem de alto nível, mantendo o conceito básico de uma linha de fonte por microinstrução. Conceitualmente, poder-se-iam escrever microprogramas em uma linguagem de alto nível comum, mas como a eficiência é crucial em microprogramas, vamos nos ater à linguagem de montagem, que definimos como uma linguagem simbólica que tem um mapeamento um-para-um para instruções de máquina. Lembre-se de que uma ineficiência de 25% num microprograma torna toda a máquina 25% mais lenta. Vamos chamar nossa microlinguagem de montagem de alto nível de "MAL" (Micro Assembly Language), que, em português, pode significar "doente", como você poderá ficar se forçado a escrever muitos microprogramas complexos para máquinas idiossincráticas. Em MAL, armazenamentos nos 16 registradores de rascunho, MAR ou MIR são representados por comandos de assinalamento. Assim, o exemplo acima, em MAL, ficaria ac := a + ac. Para indicar o uso das funções 0, 1, 2 e 3 da ALU, podemos escrever, por exemplo, ac := a + ac, a := band(ir, smask), ac := a, e a := inv(a) respectivamente, onde band significa "AND booleano" e inv significa inverso. Deslocamentos podem ser representados pelas funções lshift, para deslocamentos para a esquerda, e rshift, para deslocamentos à direita, como em tir := lshift(tir + tir) que coloca tir em ambos os barramentos A e B, realiza a adição e desloca a soma 1 bit para a esquerda antes de armazená-la de volta em tir. Desvios incondicionais podem ser manipulados com comandos goto; desvios condicionais podem testar n ou z, por exemplo: if n then goto 27 Assinalamentos e desvios podem ser combinados na mesma linha. Entretanto, um pequeno problema aparece se desejarmos testar um registrador, mas não fazer um armazenamento. Como especificamos qual registrador deve ser testado? Para resolver este problema, introduzimos a pseudovariável alu, à qual pode ser assinalado um valor apenas para indicar o conteúdo da ALU. Por exemplo, Alu := tir; if n then goto 27 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 33 Curso de Microprocessadores 0 Mar :=que pc;rd; {loop(código principal} 40= 2), Tirde := modo lshift(tir); n then goto ou 111x?} significa tir deve passar pela ALU da ALU que oifseu bit de alta{110x ordem possa ser 46; testado. Note que o uso de alu significa que ENC = 0. 1 pc := pc + 1; rd; {incremente pc} 41 Alu := tir; if n then goto 44; {1100 ou 1101?} Para indicar leituras e escritas à memória, vamos apenas colocar rd e wr no programa fonte. A 2 Ir := mbr; if n then goto 28 {salva, decodifica 42 Alu := ac; if n then goto 22; {1100 = JNEG} ordem das várias partes do comando fonte é, em princípio, arbitrária, mas, para melhorar sua legibilidade, mbr} vamos arranja-lasifnan ordem 3 Tirtentar := lshift(ir+ir); then em que são executadas. 43 Goto 0; Neste goto 19 ponto, quero reafirmar que a microlinguagem é utilizada apenas pelo projetista para criar um interpretador para as sempre 44 programaremos Por isso, 4 Tir := lshift(tir); if nmacroinstruções. then goto {000x Nós ou 001x?} Alu := ac; if z utilizando then goto 0;macroinstruções. {1101 = JNZE} 11 um único parâmetro é levado em consideração na hora de criar as microinstruções: a performance da CPU. 5 Alu := tir; if n then goto 9; {0000 ou 0001?} 45 Pc := band(ir, amask); goto 0; 46 Tir := lshift(tir); if n then goto 50; Chegamos finalmente ao ponto em que podemos juntar todos os pedaços. A figura da próxima página 7 rd; 47 Sp := sp + (-1); {1110 = CALL} é um microprograma que roda no Mic-1 e interpreta Mac-1. É um programa surpreendentemente curto 8 Ac := mbr; goto 0; 48 Mar := sp; mbr := pc; wr; apenas 79 linhas. Agora a escolha dos nomes para os49registradores de rascunho na Figura 13 é óbvia: PC, 9 Mar := ir; mbr := ac; wr; {0001 = STOP} Pc := band(ir, amask); wr; goto 0; AC SPgoto são 0;usados para armazenar os três registradores dolshift(tir); Mic-1. IRifénothen registrador de instrução 10 eWr; 50 Tir := goto {1111, examine e contém a macroinstrução que está sendo correntemente65; executada. TIR é uma cópia temporária endereço} de IR, usada código {0010 de operação três registradores seguintes contém as 11 Alupara := tir;decodificar if n then gotoo15; ou 0011?} (opcode). 51 Tir Os := lshift(tir); if n then goto constantes indicadas. AMASK é a máscara de endereços, 59; 0FFFh, e é usado para separar opcode de bits de 12 Mar := ir; rd; = ADDD} Alu :=nas tir; if n then gotoINSP 56; e DESP para isolar a endereço. SMASK é a máscara de{0010 pilha, 00FFh, e 52 é usado instruções 13 Rd; 53 Mar := ac; rd; {1111000 = PSHI} distância de 8 bits. Os seis registradores restantes não têm função assinalada e podem ser usados como o 14 Ac := mbr + ac; goto 0; 54 Sp := sp + (-1);rd; microprogramador quiser. 15 Mar := ir; rd; {0011 = SUBD} 55 Mar := sp; wr; goto 10; Como todo interpretador, o nosso microprograma tem um loop principal que busca, decodifica e 16 Ac := ac + 1; rd; {x-y = x+1 + not y} 56 Mar := sp; sp := sp + 1; rd; {1111001 = POPI} executa instruções do programa que está sendo interpretado, neste caso, instruções de nível 2. Seu loop 17 A := inv(mbr); 57 Rd; principal na 0; linha 0, onde ele inicia buscando58 a macroinstrução apontada por PC. Enquanto espera 18 Ac := começa ac + a; goto Mar := ac; wr; goto 10; que a instrução chegue, o microprograma incrementa e := continua a ativar o sinal de barramento RD. 19 Tir := lshift(tir); if n then goto {010x ou 011x?} 59 PCAlu tir; if n then goto 62; Quando 25; ela chega, na linha 2, ela é armazenada em IR e, simultaneamente, o bit de alta ordem (bit 15) é testado. 15 for a decodificação continua60na Sp linha 28; caso contrário, ela continua linha 3. 20 Alu :=Se tir;oifbit n then goto1, 23; {0100 ou 0101?} := sp + (-1); {1111010na = PUSH} 21 Alu := ac; if nenquanto then gotoque 0; a instrução {0100 =é JPOS} Mar mbr :=na ac;linha wr; goto Assumindo por um LODD,61 o bit 14:=é sp; testado 3, e10; TIR é carregado com Pc := band(ir, amask); goto 0; de {desvie} 62 Maruma := sp;vez sp := sp + 1; rd; POP} a22instrução original deslocada 2 bits para a esquerda, usando o somador {1111011 e outra= usando o 23 Alu := ac; if z then goto 22; {0101 = JZER} 63 Rd; deslocador. Note que o bit N de status da ALU é determinado pela saída da ALU, na qual o bit 14 é o de 24 Goto 0; {desvio falhou} 64 Ac := mbr; goto 0; mais alta ordem, pois IR + IR desloca IR 1 bit para a esquerda. A saída do deslocador não afeta os bits de 25 Alu := tir; if n then goto 27; {0110 ou 0111?} 65 Tir := lshift(tir); if n then goto status da ALU. 73; Todas as instruções que têm 00 em seus dois bits de alta ordem eventualmente chegam à linha 4 26 Pc := band(ir, amask); goto 0; {0110 = JUMP} 66 Alu := tir; if n then goto 70; para ter bit 13 amask); testado. As0;instruções começam 000 vãosppapa a linha começando com 27 Ac := oband(ir, goto {0111 =que LOCO} 67com Mar := sp; := sp+1; rd; 5, aquelas {1111100 = RETN} 001 vão para a linha 11. A linha 5 é um exemplo de uma microinstrução com ENC = 0; ela apenas testa TIR, 28 Tir := lshift(ir+ir); if n then {10xx ou 11xx?} 68 Rd; mas goto não o40; altera. Dependendo do resultado deste teste, o código para LODD ou STOP é selecionado. 29 TirPara := lshift(tir); if n then gotodeve {100x ou 101x?} 69a palavra Pc := mbr; goto 0; LODD, o microcódigo primeiro buscar endereçada diretamente carregando os 12 35; bits de baixa ordem de IR em MAR. Neste caso, os 4 bits de alta ordem são todos zero, mas para STOP e 30 Aluinstruções, := tir; if n then 33; {1000 1001?} 7012Abits := ac;de largura, os bits de opcode {1111101 SWAP} a outras não.goto Entretanto, comoouMAR só têm não= afetam 31 A := ir + sp; {1000 = LODL} 71 Ac := sp; escolha da palavra lida. Na linha 7, o microprograma não tem nada a fazer, e portanto apenas espera. 32 Mar := a; rd; goto 7; 72 Sp := a; goto 0; Quando a palavra chega, ela é copiada para AC e o microprograma desvia para o início do loop. STOP, 33 A := ir + sp; {1001 = STOL} 73 Alu := tir; if n then goto 76; ADDD e SUBD são similares. O único ponto notável relativo a elas é como a subtração é feita. Ela usa o 34 Mar := a; mbr := ac; wr; goto 10; 74 A = band(ir, smask); {1111110 = INSP} fato de := que 35 Alu tir; if n then goto 38; {1010 ou 1011?} 75 Sp := sp + a; goto 0; x – y == x + (-y) = x 76 + ( yA+1) x + 1 +smask); y 36 A := ir + sp; {1010 ADDL} := =band(ir, {1111111 = DESP} 37 complemento Mar := a; rd; goto 13; A adição de 1 a AC é feita 77 A := inv(a); em de dois. na linha 16, que de outra maneira seria desperdiçada 38 Aa:=linha ir + sp; {1011 = SUBL} 78 A := a + 1; goto 75; como 13. 39 Mar a; rd; goto 16; O := microcódigo para JPOS começa na linha 21. Se AC < 0, a condição de desvio falha e JPOS é 6 Mar := ir;Um rd; Exemplo de Microprograma {0000 = LODD} 2.3.2. terminado imediatamente através da volta ao 17 loop Se, entretanto, AC ≥ 0, os 12 bits de baixa Figura – Oprincipal. microprograma. ordem de IR são extraídos, fazendo um AND deles com a máscara 0FFFh e armazenando o resultado em PC. Não há nenhum custo extra para remover os bits de opcode aqui, e assim podemos também fazê-lo. Se 34 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação isso tivesse custado uma microinstrução extra, no entanto, teríamos que analisar cuidadosamente para ver se a existência de lixo nos 4 bits de alta ordem de PC poderia causar problemas mais tarde. Em certo sentido, JZER (linha 23) funciona de maneira oposta a JPOS. Com JPOS, se a condição for verificada, o desvio falha e o controle retorna ao loop principal. Com JZER, se a condição for verificada, o desvio é realizado. Como o código para realizar o desvio é o mesmo para todas as instruções de desvio, podemos economizar microcódigo simplesmente indo para a linha 22 sempre que possível. Este estilo de programação geralmente seria considerado grosseiro em outro programa de aplicação, mas num microprograma não existem barreiras. Desempenho é tudo. JUMP e LOCO são imediatas, de modo que a próxima rotina interessante a ser analisada é a LODL. Primeiro, o endereço de memória absoluto a ser referenciado é calculado somando o deslocamento contido na instrução a SP. Então, a leitura de memória é iniciada. Como o resto do código é o mesmo para LODL e LODD, vamos apenas usar as linhas 7 e 8 para ambos. Isto não apenas economiza memória de controle sem perda na velocidade de execução, mas também significa menos rotinas para depurar. Código análogo é usado para STOL, AUDL e SUBL. O código para JNEG e JNZE é similar a JZER e JPOS, respectivamente (e não ao contrário). CALL primeiro decrementa SP, então empilha o endereço de retorno, e finalmente desvia para o procedimento. A linha 49 é quase idêntica à linha 22; se fosse exatamente a mesma, poderíamos ter eliminado a 49 colocando um desvio incondicional para 22 em 48. Infelizmente, precisamos continuar ativando WR por mais uma microinstrução. Todas as macroinstruções restantes têm 1111 nos 4 bits de alta ordem, e então a decodificação dos "bits de endereço" é necessária para distingui-las. As rotinas reais são imediatas, de modo que não faremos mais comentários sobre elas. 2.3.2.1. 3.3.2.1. Comentários sobre o Microprograma Apesar de termos discutido o microprograma detalhadamente, vale a pena considerar mais alguns pontos. Na figura da página anterior, incrementamos PC na linha 1. Isso poderia igualmente ter sido feito na linha 0, liberando a linha 1 para qualquer outra coisa enquanto esperava. Nesta máquina não há nada mais a fazer, mas numa máquina real o microprograma poderia usar esta oportunidade para verificar os dispositivos de E/S esperando serviço, refrescar a RAM dinâmica, ou qualquer outra coisa. Se deixarmos a linha 1 da maneira como está, entretanto, podemos acelerar a máquina modificando a linha 8 para Mar := pc; ac := mbr; rd; goto 1; Em outras palavras, podemos iniciar a busca da próxima instrução antes de termos realmente concluído a corrente. Esta habilidade provê uma forma primitiva de pipeline7 de instruções. O mesmo artifício pode também ser aplicado às outras rotinas. É claro que uma quantidade substancial do tempo de execução de cada macroinstrução é dedicada a decodificá-la bit a bit. Esta observação sugere que poderia ser útil carregar o MPC sob o controle do microprograma. Em muitas CPUs existentes, a microarquitetura tem suporte de hardware para extrair opcodes de macroinstruções e coloca-los diretamente no MPC para realizar um desvio múltiplo. Se, por exemplo, pudéssemos deslocar IR de 9 bits para a direita, zerar os 9 bits superiores e colocar o número resultante em MPC, teríamos uma ramificação de 128 caminhos para as posições de 0 a 127. Cada uma dessas palavras conteria a primeira microinstrução para a macroinstrução correspondente. Apesar de esta abordagem desperdiçar memória de controle, ela acelera bastante a máquina e, na prática, quase sempre é utilizada. (Na verdade, isto já é utilizado desde as primeiras CPUs da Intel.) Não dissemos uma palavra sobre como a E/S é implementada. Nem temos que fazê-lo. Usando mapeamento de memória, a CPU não sabe a diferencia entre endereços de memória verdadeira e registradores de dispositivos de E/S. O microprograma manipula leituras e escritas nos espaços de endereçamento de dispositivos de E/S do mesmo modo que qualquer outra leitura ou escrita. 7 Técnica que permite a execução de mais de uma instrução ao mesmo tempo. Veremos mais sobre isto em breve. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 35 Curso de Microprocessadores 2.4 Mais um Pouco Sobre Endereçamento As instruções podem ser classificadas de acordo com o número de endereços que elas utilizam. Não se deve esquecer que um conjunto de registradores numerados da CPU constitui, de fato, uma memória de alta velocidade e define um espaço de endereçamento. Uma instrução que soma o registrador 1 ao registrador 2 deve ser classificada como tendo dois endereços porque a instrução deve especificar quais os registradores que serão adicionados, do mesmo modo que uma instrução que soma duas palavras de memória deve especificar as palavras. Instruções que especificam um, dois e três endereços são comuns. Em muitas máquinas que fazem aritmética com um endereço apenas, um registrador especial denominado acumulador provê um dos operandos. Nestas máquinas, o endereço é geralmente o endereço m de uma palavra de memória, no qual se localiza o operando. A instrução para adição com o conteúdo do endereço m faz o seguinte: acumulador := acumulador + memória[m] Instruções de dois endereços de soma utilizam um dos endereços como fonte e o outro como destino. A fonte é então somada ao destino: destino := destino + fonte As instruções de três endereços especificam duas fontes e um destino. As duas fontes são adicionadas e armazenadas no destino. Até este ponto, prestamos pouca atenção em como os bits de um campo de endereço são interpretados para se encontrar o operando. Uma possibilidade é que eles contenham o endereço de memória do operando. Outras possibilidades, entretanto, também existem, e nas seções seguintes exploraremos algumas delas. 2.4.1. Endereçamento Imediato A maneira mais simples para uma instrução especificar um operando é a parte da instrução relativa ao endereço conter realmente o operando em si em lugar do endereço ou outra informação que descreva onde o operando está. Tal operando é denominado operando imediato, pois é automaticamente buscado da memória ao mesmo tempo que a própria instrução é buscada; logo, está imediatamente disponível para uso. O endereçamento imediato tem a virtude de não requerer uma referência extra à memória para buscar o operando. Tem a desvantagem de restringir o operando a um número que possa caber em um campo de endereçamento. Em uma instrução com um endereço de 3 bits (por exemplo, campo de registradores), os operandos seriam restritos a 3 bits, o que limita a sua utilidade. As CPUs Intel não possuem um modo de endereçamento para operandos imediatos. Ao invés disso, elas possuem uma grande (uma enorme) coleção de instruções distintas nas quais um dos operandos é imediato. 2.4.2. Endereçamento Direto Um outro modo simples de especificar um operando é fornecer o endereço da palavra de memória onde o operando está contido. Esta forma é denominada endereçamento direto. Os detalhes de como a CPU sabe quais endereços são imediatos e quais são diretos não serão discutidos. Geralmente existem duas abordagens: utilizar códigos de operação diferentes ou utilizar um modo de endereçamento especial para cada tipo de operando. 2.4.3. Endereçamento de Registrador Endereçamento de registrador é conceitualmente a mesma coisa que endereçamento direto. Nesta forma de endereçamento, o campo de endereço contém o número do registrador no qual o operando é armazenado. Uma máquina com 16 registradores e 65.536 palavras de memória possuí, na realidade, dois espaços de endereçamento. Deve-se pensar no endereço de tal máquina como tendo duas partes: (a) um bit que diz se deseja-se um registrador ou uma palavra da memória (b) um campo de endereço que diz qual registrador ou palavra de memória é desejado(a). Como existem menos registradores que palavras de 36 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação memória, precisa-se de um endereço menor, e assim instruções de diferentes formatos são freqüentemente utilizadas para operandos tipo registrador e operandos tipo memória. Se existisse uma instrução de registrador para cada instrução que endereçasse memória, a metade dos códigos de operação seria para operandos tipo memória e a outra metade para operandos tipo registrador. Seria necessário um bit no código de operação para designar qual o espaço de endereçamento a ser utilizado. Se este bit fosse removido do campo código de operação e colocado no campo de endereçamento, o fato de dois espaços de endereçamento estarem sendo utilizados ficaria mais claro. O bit indicaria então qual o espaço de endereçamento a ser usado. As máquinas foram projetadas com registradores por duas razões: (a) os registradores são mais rápidos do que a memória principal e (b) como são em pequeno número, são necessários apenas alguns bits para endereça-los. Infelizmente, ter 8 ou 16 registradores também complica muito a programação, pois deve-se decidir quais operandos e resultados intermediários devem ser guardados no número limitado de registradores e quais devem ser guardados na memória principal. 2.4.4. Endereçamento Indireto Endereçamento direto é um esquema em que o endereço especifica qual palavra de memória ou registrador contém o operando. Endereçamento indireto é um esquema em que o endereço especifica qual palavra de memória ou registrador contém não o operando, mas o endereço do operando. Como exemplo, considere uma instrução para carga de um registrador (que denominaremos R1) indiretamente da posição de memória 1000, onde a posição 1000 de memória contém 1510, como mostra a Figura 18(b). (a) (b) Figura 18 – Comparações entre os endereçamentos direto e indireto. (a) Endereçamento direto. (b) Endereçamento indireto. Primeiramente, o conteúdo da posição 1000 é copiado para um registrador interno da CPU. Este número de 16 bits (1.510) não é colocado em R1. Se fosse como na Figura 18(a) teríamos uma instrução de endereçamento direto. Ao em vez disso, o conteúdo da posição 1510 de memória é buscado e colocado em R1. O número contido na posição 1000 não é o operando, mas em vez disso "aponta" para o operando. Por isso, é denominado apontador. Alguns processadores, embora nenhum dos vistos durante o nosso curso, permitem endereçamento indireto de múltiplos níveis. Neste modo de endereçamento, um apontador é utilizado para localizar uma palavra de memória que por sua vez aponta para uma palavra de memória, e assim por diante. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 37 Curso de Microprocessadores 2.4.5. Indexação Muitos algoritmos necessitam da execução de alguma operação sobre uma seqüência de estruturas de dados armazenadas em posições consecutivas de memória. Por exemplo, um bloco de n palavras de máquina ocupando as posições. A, A+1, A+2, ..., A+n-1 que devem ser copiadas nas posições B, B+1, B+2, ..., B+n-1. Este problema pode ser resolvido através de endereçamento indireto. Um registrador ou posição de memória é carregado(a) com o endereço A; um(a) outro(a)‚ carregado(a) com o endereço B. As instruções MOVE utilizam esses dois endereços como apontadores. Após cada palavra ter sido copiada, os apontadores para elas são incrementados de 1. Os apontadores fazem parte dos dados e, obviamente, não fazem parte do programa. Uma outra solução é ter um ou mais registradores, denominados registradores de índice, que funcionam da seguinte maneira. Os endereços possuem duas partes: o número de um registrador de índice e uma constante. O endereço do operando é a soma da constante com o conteúdo do registrador de índice. No exemplo acima, se ambos os endereços são indexados utilizando um registrador de índice que contém o inteiro k, a instrução MOVE A,B copiará o conteúdo da posição de memória A + k para B + k. Inicializando o registrador de índice com o valor 0 e incrementando-o do tamanho da palavra, após cada palavra ser copiada, apenas um registrador é necessário para o loop de cópia. Além disso, incrementar um registrador é mais rápido que incrementar uma posição de memória. A indexação é também normalmente utilizada para endereçar um campo com um deslocamento conhecido, em relação ao início de uma dada estrutura. Variáveis locais em um procedimento são acessadas desta maneira. 2.5 Nossa Macroarquitetura Real Neste curso, usaremos o 68HC11A8 da Motorola como exemplo de macroarquitetura real. O 68HC11A8 pertence a família de microcontroladores 68HC11. Em breve, veremos as características de cada membro desta família. Neste capítulo, não conheceremos os periféricos extras que fazem do 68HC11 um microcontrolador. Darei enfoque apenas as características da CPU que pertencem a (quase) todos os microcontroladores 68HC11XX. 2.5.1. Conjunto de Registradores O 68HC11 é uma CPU de 8 bits por isso suas instruções trabalham (quase todas) com operandos de 8 bits. Ao contrário da nossa CPU (o Mac-1), o 68HC11 possui dois acumuladores de 8 bits (A e B). Na maioria das instruções podemos utilizar o acumulador A ou B, indiferentemente, com algumas exceções. Por exemplo, as instruções ABX e ABY somam o conteúdo do acumulador B ao dos registradores de índice X e Y, respectivamente, não havendo instrução que faça o mesmo com o registrador A. As instruções TAP e TPA são usadas para transferir dados do acumulador A para o registrador de código de condição, CCR (como veremos a seguir), ou vice-versa. Entretanto, não há instrução equivalente que use o acumulador B, ao invés de A. Os acumuladores A e B podem ser combinados para formar um registrador de 16 bits (D). Este registrador é sobretudo utilizado em operações de multiplicação de dois operandos de 8 bits (para armazenar o resultado), e de divisão de dois operandos de 16 bits (para armazenar o resto). 0 68HC11 possui ainda dois registradores de índice de 16 bits (X e Y), que são usados principalmente com instruções que utilizam endereçamento indexado. Veremos em breve que operações que trabalham com o indexador Y são um ciclo de máquina mais lentas, quando comparadas com as que trabalham com o indexador X. O PC e o SP também são de 16 bits. Neste ponto, você pode estar desconfiando que esta CPU trabalha com endereços de 16 bits. Fique sabendo que isto está correto. Desse modo, o 68HC11A8 pode endereçar até 64 kbytes memória. O SP pode ser localizado em qualquer lugar dentro desta faixa de 38 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação endereços. Normalmente, o apontador de pilha SP é inicializado por uma das primeiras instruções do macroprograma. O HC11 utiliza E/S mapeada em memória. Como já sabemos, neste caso, a CPU não destingue entre operações na memória e em um dispositivo de E/S. Além disso, este espaço de endereçamento é dividido entre a memória e os dispositivos de E/S do sistema, ou seja, só teremos realmente 64 kbytes de memória se não tivermos nenhum dispositivo de E/S. Um registrador novo para você é o CCR (Registrador de Código de Condição). Em qualquer instante de tempo, os bits deste registrador nos dão informações importantes sobre a CPU. Na Tabela 3 é apresentado o significado de cada um dos bits do CCR. O bit I (se igual a 1) impede que a CPU receba pedidos de interrupção (veja a Seção 1.6) dos dispositivos de E/S do sistema. Isto pode ser útil, por exemplo, quando a CPU está realizando operações críticas. O bit H indica a ocorrência de vai-1 do bit 3 para o 4, após o resultado da última operação. É útil quando se está trabalhando com valores em BCD. A função dos bits N, Z, V e C deve ser clara para você. Estes bits (incluindo o H) são usados para os programas de nível 2 tomarem decisões, através de instruções de desvios condicionais que testam estes bits. Os bits S e X vão ser vistos mais adiante. Figura 19 – Conjunto de registradores do 68HC11. Como você descobrirá, o registrador CCR provê uma maneira rápida de controlar alguns aspectos da CPU e verificar o resultado das operações (para que decisões possam ser tomadas). Este registrador existe na maioria das CPUs, algumas vezes com outro nome (nas CPUs da Intel ele geralmente é chamado de FLAGS) e/ou tamanho diferente. S Desabilita o modo STOP N Indica se a última operação resultou em zero X Desabilita interrupções do pino XIRQ Z Indica se a última operação resultou em zero H Indicador de vai-1 do nibble menos significativo V Indica se a última operação resultou em overflow I Desabilita todas as interrupções mascaráveis C Indica a existência de vai-1 ou sobra-1 Tabela 3 – Significado dos bits do registrador CCR. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 39 Curso de Microprocessadores No 68HC11, assim como na maioria das CPUs, o número de registradores é bastante reduzido. Desde que eles são, geralmente, mais rápidos que a memória principal, a tarefa de decidir quais dados ficaram na memória e quais ficaram nos registradores é bastante “filosófica”. 2.5.2. Pinagem e Sinais de Barramento O 68HC11 é geralmente encontrado em um encapsulamento PLCC de 52 pinos. A maioria desses pinos pertencem aos seus dispositivos de E/S internos. Por enquanto, apenas os pinos de endereço, dados e controle nos interessam. Como na maioria das CPUs, os pinos de dados do HC11 são multiplexados com os de endereço. Mais especificamente, os 8 pinos de dados são multiplexados com os 8 pinos que correspondem aos 8 bits menos significativos de um endereço de 16 bits (ufa!). Para explicitar a multiplexação, este pinos são nomeados de AD0 à AD7. Os instantes nos quais estes pinos possuem informações de endereço ou do dado, são distintos através de um pino chamado AS (Address Strobe). Durante o período em que AD0-AD7 contêm a parte menos significativa do endereço, AS permanece ativo. Como veremos em breve, AS pode ser utilizado para habilitar a carga de um registrador externo à CPU (por exemplo, o 74373) que irá reter o endereço quando AS tornar-se inativo. Um pino chamado R/ W é utilizado para distinguir entre ciclos de leitura e ciclos de escrita. Como você deve estar adivinhando, R/ W é colocado em nível alto durante uma leitura, e em nível baixo durante as escritas. Já vimos que, como todo sistema digital, uma CPU necessita de um relógio externo (uma base de tempo). No 68HC11 esta base de tempo deve ser uma onda quadrada com freqüência de no máximo 8 MHz. Esta base de tempo aparece dividida por 4 no pino de saída E. Este pino deve ser usado para temporizar as operações com o barramento, ou seja, o tempo em que E está baixo é utilizado para estabilizar todos os sinais necessários (AD0-AD7, AS, R/ W , etc), em seguida, a operação de E/S deve ser realizada enquanto E estiver alto. Isto significa, no caso de uma base de tempo de 8 MHz, que uma operação de leitura/escrita deve ser realizada em 0,5 µs. Como toda operação interna no 68HC11 demora também 0,5 µs para ser realizada, isto quer dizer que uma macroinstrução do 68HC11 demora, no mínimo, 1 µs para ser executada (0,5 µs para ser buscada na memória e 0,5 µs para ser executada). Apesar da freqüência máxima da base de tempo do HC11 ser 8 MHz, podemos utilizar uma base de tempo menor. Contudo, qual o motivo de queremos uma base de tempo mais lenta? A tecnologia utilizada na construção é a HCMOS (High speed Complementary Metal-Oxide, ou seja, metal, óxido e semicondutor na configuração complementar de alta velocidade). Além de outras características, esta tecnologia apresenta a vantagem de baixo consumo. O seu consumo é tanto maior quanto a freqüência da sua base de tempo. Por isso, em aplicações que não necessitam de uma velocidade elevada, a base de tempo pode ser reduzida para economizar energia. Isto é útil em sistema alimentados por bateria (como em um controle remoto ou um telefone sem fio). 2.5.3. Modos de Endereçamento A CPU M68HC11 utiliza seis modos de endereçamento relacionados com a memória, são eles: Imediato, Direto, Estendido, Indexado, Inerente e Relativo. Cada modo de endereçamento trás informações para gerar uma palavra de dois bytes, que compõem o chamado endereço efetivo do operando. Estas palavras resultam da declaração feita no campo do operando, e são elas que aparecem no barramento de endereço durante a execução de uma instrução. 2.5.3.1. Endereçamento Imediato (IMM) No modo imediato o operando pode ser uma constante de 8 ou 16 bits, dependendo do comprimento do registrador envolvido. Para especificar o modo imediato, o mnemônico precisa ser seguido por um caractere ‘#’. Por exemplo, “LDAA #256” ou “LDX #$C000”. No exemplo anterior o símbolo “$” foi utilizado para especificar que o operando está expresso em hexadecimal. Também podem ser utilizados os símbolos “ ’ ”, “%” e “@”, para especificar caractere ASCII, binário e octal, respectivamente. 40 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação 2.5.3.2. Endereçamento Estendido (EXT) É equivalente ao endereçamento direto do Mac-1. Para especificar este modo, o mnemônico da instrução deve ser seguido por um endereço de 16 bits. Novamente, o endereço pode ser expresso em hexadecimal, binário, octal, caractere ASCII ou decimal. 2.5.3.3. Endereçamento Direto (DIR) É semelhante ao endereçamento estendido, porém, o operando deve localizar-se na memória entre o endereço 0000h e 00FFh. Por isso, o mnemônico da instrução deve ser seguido apenas pelos 8 bits menos significativos do endereço efetivo. Deste modo, o comprimento das instruções é um byte menor, quando comparadas com as instruções que utilizam endereçamento estendido. Isto quer dizer que as instruções que utilizam o endereçamento direto são um ciclo mais rápidas que as que usam endereçamento estendido. O modo Direto é, as vezes, chamado de modo de endereçamento da página zero. O comprimento da maioria das instruções nesse modo é de 2 bytes: um para o código de operação e outro para o endereço. Somente quatro instruções no modo direto requerem um byte extra, são aquelas que envolvem o registrador Y. 2.5.3.4. Endereçamento Indexado (IND) No modo de endereçamento indexado, o registrador X ou o Y é utilizado no cálculo do endereço efetivo, que é variável, sendo dependente do conteúdo de X ou de Y e, também, dos 8 bits de offset contidos na instrução. Este modo de endereçamento pode ser usado para fazer referência a alguma locação de memória dentro dos 64 Kbytes endereçáveis. Neste caso as instruções têm geralmente 2 ou 3 bytes: o código de operação e oito bits de offset, mais o prebyte ou não. Para especificar o modo indexado, o mnemônico precisa ser seguido por “IND,X” ou “IND, Y”. Onde “IND” é uma constante de 8 bits, X e Y são os indexadores. Por exemplo, “LDAA $56,X”. 2.5.3.5. Endereçamento Inerente (INH) Uma instrução no modo de endereçamento inerente opera apenas com registradores internos de uso geral (Figura 19), não dependendo de uma especificação de endereço. Os operandos são os próprios registradores e assim não são procurados na memória. Isto significa que o próprio código de operação determina o que a CPU pode fazer. Essas instruções são geralmente de um ou dois bytes. Muitas instruções da MCU, usam um ou mais registradores como operandos. Por exemplo, a ABA causa a soma dos conteúdos de A com B, colocando o resultado no acumulador A. A INCB promove o incremento do conteúdo do registrador B. De modo análogo, a instrução INX incrementa o conteúdo de X. 2.5.3.6. Endereçamento Relativo (REL) O endereçamento relativo é usado principalmente pelas instruções branch (desvio). Estas instruções geram 2 bytes de código de máquina: um para o código de operação e outro para o offset relativo. É desejável que os desvios ocorram em ambas as direções, por isso o byte de offset é sinalizado em complemento de dois, abrangendo o domínio de -128 a +127 bytes (com relação ao endereço da próxima instrução apontada pela branch). O offset é sempre o último byte de uma instrução branch. Se ele for zero, a execução do programa continuará com a instrução imediatamente após a branch. Uma instrução de desvio do tipo BRA (BRanch Always), por exemplo, com offset FEh resultaria num loop infinito sobre ela mesma. Nos modos direto ou indexado por X (INDX), as instruções BRCLR e BRSET (Tabela 4) são de 4 bytes; então um offset $FC causará uma repetição na execução da instrução, até que o bit de teste torne-se falso. Essas mesmas instruções no modo indexado Y (INDY) têm 5 bytes; assim, um offset $FB causa uma repetição da execução da instrução, até que o bit de teste torne-se falso. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 41 Curso de Microprocessadores 2.5.4. Conjunto de Instruções Na Tabela 4 é apresentado o conjunto de macroinstruções dos microcontroladores da família HC11. Como você pode notar, este microcontrolador apresenta um número de instruções bem maior que o nosso Mac-1. A Tabela 4 utiliza a notação descrita na Figura 20. Figura 20 – Notação utilizada pela Tabela 4. Na primeira coluna da Tabela 4 encontramos o mnemônico da instrução. Também está especificado se a instrução necessita de operandos ou não. Na segunda e terceira colunas, existe uma breve descrição da instrução. Os modos de endereçamento que podem ser utilizados com uma dada instrução é apresentado na coluna quatro. Para algumas instruções, nem todos os modos de endereçamento estão disponíveis. Em especial, algumas instruções (por exemplo, a instrução BRSET) não aceitam o modo estendido. Nestes casos, geralmente temos que utilizar indexação. Por isto, muita atenção quando for especificar os operandos de uma instrução. Sempre verifique se a instrução aceita o endereçamento que você deseja utilizar. Na quinta e sexta colunas encontramos os códigos de operação da instrução. Uma mesma instrução pode ter vários códigos de operação (um para cada modo de endereçamento suportado pela instrução). Em particular, para especificar a indexação por Y é utilizado um prefixo (geralmente a constante 18h). Ou seja, todas as instruções que utilizam indexação em Y possuem um byte a mais (como pode ser comprovado pela coluna sete). É por isto que as instruções que trabalham com o indexador Y são um ciclo de máquina mais lentas, quando comparadas com as que trabalham com o indexador X. A coluna sete informa quantos ciclos de instrução são utilizados para executar uma determinada instrução. Por exemplo, instruções como INCA são executadas em apenas dois ciclos, enquanto que as de divisão levam 41 ciclos. Isto é característico das instruções de nível 2. Pois, as instruções de nível 1 são executadas diretamente pelo hardware em apenas um ciclo. A coluna oito não nos interessará. Já a nove é muito importante. Ela ilustra como os bits do registrador CCF podem ser afetados por uma determinada instrução. Isto é útil quando utilizamos instruções de salto condicional. Quando fiz meu primeiro curso de inglês, meu professor ensinou-me um “macete” para aprender os verbos irregulares da língua inglesa: decorá-los. O mesmo artifício pode ser utilizado no aprendizado das instruções do 68HC11. Felizmente, com um pouco de prática, o que poderia parecer complicado, torna-se claro e natural. Além disso, o grande incentivo para aprendermos as instruções de uma CPU é que todas elas, independente da família e do fabricante, possuem um conjunto de instruções muito parecido. Isto significa que, uma fez que tenhamos entendido as instruções de uma CPU, com mais um mínimo de esforço, poderemos programar qualquer CPU. 42 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação 2.6 Comentários Apesar de ter pouca experiência na programação de microprocessadores, acredito que agora você possui um conhecimento bastante sólido de como uma CPU qualquer trabalha. Até o fim do curso, com a realização de programas com o 68HC11, este conhecimento aumentará bastante. Não fique preocupado se você não compreendeu a maioria das instruções da Tabela 4. Acredito que a melhor forma familiarizar-se com estas instruções é praticando. Por isto, não perdi tempo explicando-as uma por uma. De nada adiantaria a explicação teórica sem a prática. Além disso, esta explicação poderia deixa-lo com a falsa impressão de já saber programar em assembler. Então, acredito que esta falta inicial de respostas o levará ao laboratório e a fazer cada vez mais perguntas. E está é a melhor forma de aprender: praticando e perguntando. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 43 Curso de Microprocessadores Tabela 4 – Conjunto de macroinstruções do 68HC11. 44 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação Tabela 4 – Conjunto de macroinstruções do 68HC11. (Continuação) Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 45 Curso de Microprocessadores Tabela 4 – Conjunto de macroinstruções do 68HC11. (Continuação) 46 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação Tabela 4 – Conjunto de macroinstruções do 68HC11. (Continuação) Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 47 Curso de Microprocessadores Tabela 4 – Conjunto de macroinstruções do 68HC11. (Continuação) 48 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 2- Microprogramação Tabela 4 – Conjunto de macroinstruções do 68HC11. (Continuação) Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 49 Curso de Microprocessadores Capítulo 3 - Entrada e Saída Como discutido nas Seções 1.2 e 1.6, um microprocessador sem dispositivos de E/S não é algo muito útil. Isto acontece porque a função básica de uma CPU é processar dados. Evidentemente, ela necessita receber os dados a serem processados de alguma fonte externa. Esta pode ser um sistema físico, um sistema eletrônico, ou mesmo um usuário. Da mesma forma, após realizar o processamento necessário, o microprocessador tem que enviar o resultado deste processamento ao mundo exterior. Novamente, o destino dos dados pode ser um sistema físico, um sistema eletrônico, ou o usuário do sistema. Se nos pensarmos um pouco na grande variedade de aplicações para microprocessadores, podemos concluir que, quando se trabalha com microprocessadores, existem várias possibilidades de fontes e destinos de dados. Para cada fonte de dados, devemos utilizar o dispositivo de entrada apropriado. Da mesma forma, para cada destino devemos utilizar o dispositivo de saída mais conveniente às nossas necessidades. Como vimos na Seção 1.6, os controladores de dispositivos tornam o “diálogo” entre a CPU e os dispositivos de E/S bastante padronizado. De forma que, para nos tornarmos um bom projetista de sistemas microprocessados, não precisamos realmente conhecer uma infinidade de dispositivos de E/S. De fato, o que for visto durante este curso já será bastante abrangente. Dependendo da complexidade, um sistema microprocessado pode possuir uma infinidade de dispositivos E/S. Desde que estes dispositivos têm um papel fundamental no sistema, necessitamos de técnicas que nos ajude a utiliza-los da maneira mais eficiente possível. Para isto, três técnicas são mais populares: E/S programada, E/S por interrupção e E/S via DMA. A E/S programada é a técnica mais simples, porém é a que mais sobrecarrega a CPU. Já a E/S via DMA, é a mais rápida, porém é a mais complexa e não pode ser utilizada com todos os dispositivos de E/S. Essas três técnicas serão vistas em detalhes neste capítulo e praticadas em laboratório (com exceção da E/S via DMA). No final do capítulo, veremos em detalhes duas portas de comunicação bastante utilizadas no projeto de sistemas: a porta serial e a porta paralela. Estas portas são bastante diferentes entre si, mas possuem a mesma função: interligar a CPU com dispositivos de E/S (daí o nome porta). Na verdade, como veremos, estas portas não são utilizadas apenas para conectar dispositivos de E/S, também podem interligar sistemas entre si. Por último, iniciaremos nosso estudo de conversores A/D e D/A. Estes dispositivos fazem a interface entre o mundo digital dos microprocessadores e o nosso mundo analógico. Devido a complexidade de alguns aspectos (e a importância de outros), os conversores A/D e D/A possuem um capítulo inteiro dedicado a eles. 3.1 E/S Programada Reporte-se à Seção 1.6 (página 16), lá está descrito como deve ser realizada a E/S com a ajuda dos controladores de dispositivos. Mais especificamente, estamos interessados no trecho re-apresentado a seguir: “...devemos saber que quando utilizamos dispositivos de E/S, como regra geral, primeiro a CPU deve acessar os registradores de controle para configurar o dispositivo de E/S. Em seguida, caso quisermos enviar ou receber dados, a CPU precisa ler as informações contidas nos registradores de status. Só assim, ela poderá receber ou enviar os dados desejados, através do registrador de dados.” Na verdade, o que está descrito acima é o processo de E/S programada. Como exemplo, observe o trecho de programa escrito em linguagem C a seguir: key = !ESC; do{ Faça algo If(kbhit()) key = getch(); Faça mais alguma coisa }while(key != ESC) 50 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída O programa anterior utiliza a função kbhit(). Esta função verifica se alguma tecla do teclado foi pressionada. Em caso positivo, ela retorna verdadeiro, caso contrário, retorna falso. O nosso programa exemplo fica dentro de um loop executando várias tarefas. Uma dessas tarefas verifica se uma tecla foi pressionada. Se positivo, o programa lê o teclado e armazena a tecla pressionada na variável key. Apenas quando a condição key != ESC não for satisfeita o programa sai do loop. Dois aspectos importantes do programa anterior devem ser observados. Primeiro, a instrução que verifica se alguma tecla foi digitada (instrução de E/S) deve ser incluída entre as instruções normais do programa. Caso contrário, a tecla (o dado) jamais será recebida. Segundo, a maior parte do tempo de processamento da E/S é gasto na verificação da existência do dado, pois a maioria das vezes que o programa verifica se uma tecla é pressionada o resultado é negativo. Isto quer dizer que muito tempo de processamento é desperdiçado. Em algumas aplicações, este é o maior pecado da E/S programada. Observe agora o próximo programa exemplo. O programa precisa receber uma tecla do teclado (logo, realizar uma operação de E/S). Contudo, a tecla é indispensável para a realização de suas tarefas. Por isso, ele só passa para a próxima instrução quando uma tecla houver sido digitada. Neste caso, não nos interessa se algum tempo está sendo desperdiçado. Temos todo o tempo do mundo. Por isso, esta parece ser a técnica mais adequada neste caso. do{ puts(“Especifique a switch(getch()){ case 1 : faça case 2 : faça case 3 : faça case 4 : faça }while(1); operação:”); algo; outra outra outra break; coisa; break; coisa; break; coisa; Que fique claro que os programas em linguagem C apresentados nesta seção são apenas ilustrativos. Na verdade, como veremos em breve, kbhit() e getch() não realizam E/S exatamente como descrito na Seção 1.6, ou seja, através dos registradores de status e de dados do controlador de dispositivo do teclado. Contudo, os programas ilustram bem a filosofia da E/S programada. Vimos um caso em que a E/S programada é a mais indicada. Agora, considere um sistema microprocessado que possua dez dispositivos de E/S. Considere também que qualquer dispositivo pode ter um dado novo em qualquer instante de tempo. Isto significa que a CPU deve checar o registrador de status de todos os dispositivos de E/S, o mais rápido possível. Além disso, se dois ou mais dispositivos de E/S contiverem dados novos em um mesmo instante de tempo, existe a possibilidade de perda de dados por parte de um dispositivo mais crítico? Se existe, posso dar prioridade a este dispositivo? Pelo exemplo anterior, não é preciso ser nenhum gênio para descobrir que a E/S programada não é indicada para sistemas com muitos dispositivos ou com dispositivos críticos. Neste caso, a escolha mais indicada talvez seja E/S por interrupção. 3.2 Interrupção A técnica de E/S por interrupção é muito importante para a operação de sistemas microprocessados, pois ela possibilita ao microprocessador responder rapidamente aos seus dispositivos de E/S. A importância dessa abordagem pode ser entendida comparando-a a uma campainha. Se uma porta não tiver nenhum dispositivo para chamar a atenção (como uma campainha), talvez seja necessário irmos até ela para verificar se existe alguém à porta. Com uma campainha, necessitamos ir até a porta apenas quando a campainha tocar. Do mesmo modo, não é eficiente esperar que o microprocessador verifique se seus periféricos requerem atenção (como na E/S programada). Para isto, existem as interrupções, que são como campainhas que avisam ao microprocessador que algum dispositivo necessita dele. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 51 Curso de Microprocessadores 3.2.1. Modo de Operação Como visto anteriormente, durante a execução normal de um programa, as instruções são lidas da memória e executadas de acordo com o fluxo normal do programa. O processador usa um registrador especial chamado contador de programa, PC, para sinalizar a próxima instrução a ser executada. Um conjunto de registradores de propósito geral são usados para manipular e armazenar temporariamente algum dado usado pelo programa. A maioria das CPUs possuem um pino de entrada que quando ativado, faz com que a CPU execute a corrente instrução e salve o PC, juntamente como todos os outros registradores, na pilha. Em seguida, o fluxo de execução do programa é desviado para uma rotina especial chamada de rotina de tratamento de interrupção. Nesta rotina encontra-se todas as instruções necessárias para atender as necessidades do dispositivo que requisitou a interrupção. Este processo pode envolver, por exemplo, a transferência de dados de um dispositivo para uma região na memória. A última instrução executada pela rotina de interrupção é uma instrução de retorno (RTI). Esta instrução força o microprocessador a restaurar o PC e todos os demais registradores, com os valores salvos na pilha. Dessa forma, após atender à interrupção, o microprocessador volta ao estado que estava antes da requisição de interrupção (IRQ). Por isso, talvez a palavra que melhor defina o processo de interrupção seja “transparência”. Devido a esta característica, a técnica de interrupção não é utilizada apenas na E/S. Ela pode ser utilizada para sincronizar eventos, ou para responder a eventos externos. Sobre tudo, como veremos em breve, ela é bastante utilizada na aquisição de dados. 3.2.1.1. Vetor de Interrupção O processo de “chamar” uma função ou uma subrotina qualquer envolve dois passos. Primeiro, o PC, ou seja, o endereço de retorno, é salvo na pilha. Segundo, o endereço no qual se localiza a rotina ou função é colocado dentro do PC (isto é realmente o que constitui o salto). Com uma rotina de interrupção, o primeiro passo é substituído pelo empilhamento de não apenas o PC, mas todos os registradores da CPU. E o segundo passo? Ou seja, de onde a CPU deve obter o endereço da rotina de interrupção para efetuar o salto? A resposta é: do vetor de interrupção. O vetor de interrupção é uma região de memória contínua que guarda o endereço de todas as rotinas de interrupção de uma CPU. Por exemplo, se uma CPU suportar até dez pedidos de interrupção diferentes, ela deve possuir em sua memória um vetor de endereços de dez posições. Caso esta mesma CPU possa endereçar até 64 Kbytes de memória, ela deverá ter reservado 10 x 2 bytes = 20 bytes para o vetor de interrupção. Cada posição do vetor de interrupção está relacionado com apenas uma fonte de interrupção. Ou seja, sempre que ocorre uma determinada interrupção, a CPU já sabe qual é a posição do vetor de interrupção que se encontra o endereço da rotina de interrupção relacionada. Na verdade, dois dispositivos podem compartilhar a mesma interrupção. Para isto, basta que a rotina de interrupção verifique qual dispositivo fez a requisição. Como? Verificando o registrador de status dos dispositivos que compartilham a interrupção. Outra maneira, bem mais explícita, é utilizando o esquema da Figura 21. Nesta figura, uma única linha de IRQ (pino INT , Figura 21) pode ser compartilhada por até oito dispositivos. Os pedidos de interrupção (I1 à I8), além de ativar o pino INT , habilitam a carga do 74LS374 (registrador de 8 bits). Com isso, a rotina de interrupção pode verificar qual dispositivo requisitou ajuda. Agora, surge outra dúvida: “Quem coloca o endereço da rotina de interrupção na posição correta do vetor?”. Além de escrever a rotina de interrupção, somos responsáveis também por colocar o endereço desta rotina no vetor de interrupção. Para a maioria das CPUs, o vetor de interrupção localiza-se em um endereço fixo. Para outras, como as CPUs Intel mais avançadas, esta localização é dada por um registrador que aponta para o vetor de interrupção. A localização exata de uma dada IRQ deve ser conseguida consultando-se o manual da CPU. 52 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída Figura 21 – Esquema de compartilhamento de um pino de IRQ por até oito dispositivos. Para formalizar o processo de interrupção, os passos que constituem o processo são listados a seguir: O dispositivo que necessita de auxílio da CPU ativa um sinal ( IRQ ) em um de seu pinos, requisitando uma interrupção; Ao receber o sinal, a CPU executa a corrente instrução e salva todos os seus registradores na pilha; Neste momento, a CPU pode ativar um sinal, geralmente chamado IACK (Interrupt ACKnowledge), avisando ao dispositivo que a interrupção já vai ser atendida; Em seguida, a CPU localiza o endereço da rotina de interrupção no vetor de interrupção e coloca-o no PC; A rotina de interrupção é executada, se necessário, o dispositivo requerente,e; Os registradores são retirados da pilha, ou seja, a CPU volta ao curso normal do programa. 3.2.1.2. Tempo de Latência Da Seção 3.2.1.1, podemos notar que o processo de interrupção requer um certo tempo de processamento. Este tempo é necessário para que a CPU salve os registradores na pilha e vá para a rotina de tratamento de interrupção. Além disso, após a rotina de interrupção ser executada, a instrução RTI restaura o conteúdo de todos os registradores, ou seja, mais processamento é consumido. Por isso, durante o projeto do sistema o tempo de sobre carga, conhecido também como tempo de latência, precisa ser estimado. Se o tempo de latência for muito elevado para a aplicação, outro método tem que ser estudado. Por exemplo, suponha que uma CPU gaste 13 µs para salvar os registradores na pilha e colocar o endereço da rotina de interrupção no PC. Além disso, considere que sejam gastos 10 µs para restaurar os registradores. Neste caso, o tempo de latência seria 23 µs. Isto tem duas implicações: Toda interrupção levará 13 µs para ser atendida, e; Toda rotina de interrupção levará mais que 23 µs para ser executada. Se, por exemplo, o código dentro da rotina levar 10 µs para ser processado, a periodicidade da interrupção pode ser no mínimo 33 µs. No caso de um sistema de aquisição de dados, o intervalo mínimo entre uma aquisição e outra será 33 µs. Caso qualquer uma das implicações anteriores seja um problema, outra técnica deverá ser utilizada. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 53 Curso de Microprocessadores 3.2.1.3. Prioridade8 Se um sistema possui apenas um dispositivo de E/S, então as interrupções funcionam sempre como descrevemos, e não há nada mais a dizer sobre elas. Entretanto, um sistema real pode possuir muitos dispositivos de E/S e vários deles podem estar em atividade simultaneamente. Existe uma probabilidade não nula de que, no momento em que uma rotina de interrupção está em execução, um segundo dispositivo de E/S deseje gerar a interrupção dele. Existem duas abordagens para este problema. Na primeira, todas as rotinas de interrupção inibem interrupções subseqüentes antes de fazer qualquer coisa, mesmo antes de salvar os registradores. Esta abordagem é muito simples, pois as interrupções são consideradas estritamente de maneira seqüencial, mas pode causar problemas quando os dispositivos não podem tolerar muito atraso. Por exemplo, em uma linha de comunicação de 9600 bps, os caracteres chegam a cada 1.042 µs, estando pronto para receber ou não. Se o primeiro caractere não foi ainda processado quando o segundo chega, podem-se perder dados. Quando um sistema possui dispositivos de E/S críticos em relação ao tempo, uma abordagem mais adequada de projeto é atribuir diferentes prioridades para diferentes dispositivos de E/S, altas para dispositivos muito críticos e baixas para dispositivos menos críticos. Quando um dispositivo de prioridade n interrompe, a rotina de interrupção deve também executar com prioridade n. Quando uma rotina de interrupção da prioridade n está em execução, qualquer tentativa de um dispositivo com uma prioridade menor causar uma interrupção é ignorada até que a rotina de interrupção tenha terminado e a CPU recomece a executar o programa principal (Prioridade 0). Por outro lado, deve-se permitir que interrupções de dispositivos de prioridade mais altas aconteçam sem atraso. Com as próprias rotinas de interrupção sujeitas a interrupção, a melhor maneira de administrá-las corretamente é assegurar que todas as interrupções sejam transparentes. Consideremos um exemplo simples de múltiplas interrupções. Um computador tem três dispositivos de E/S: uma impressora, um disco e uma interface RS-232, com prioridades 2, 4 e 5, respectivamente. Inicialmente, em t = 0, o programa principal está em execução quando, de repente, em t = 10, uma interrupção da impressora acontece. A rotina de serviço de interrupção da impressora é iniciada, como mostra a Figura 22. Em t = 15, a interface RS-232 requer atenção e gera uma interrupção. Como a interface RS-232 tem uma prioridade (5) maior que a da impressora (2), a interrupção acontece. O estado da máquina, que está agora executando a rotina de serviço de interrupção da impressora, é empilhado e a rotina de serviço de interrupção da interface RS232 iniciada. Um pouco mais tarde, em t = 20, o disco está pronto e quer serviço. Entretanto, sua prioridade (4) é menor do que a da rotina de interrupção (5) correntemente em execução, de forma que o hardware da CPU não reconhece (acknowledge) a interrupção, e esta permanece pendente. Em t = 25, a rotina da RS232 termina, assim ela retorna ao estado em que estava logo antes da interrupção da RS232 ter acontecido, ou seja, executando a rotina de serviço de interrupção da impressora em prioridade 2. Tão logo a CPU chavear para a prioridade 2, antes mesmo de uma instrução poder ser executada, a interrupção de disco com prioridade 4 é permitida, e a rotina de interrupção do disco inicia a sua execução. Quando ela termina, a rotina de impressora retorna a sua execução. Finalmente, em t = 40, todas as rotinas de serviço de interrupção terminaram e o programa principal continua a partir de onde tinha sido interrompido. Por simplicidade, algumas CPUs possuem apenas um ou dois níveis de prioridade (como as CPUs da Intel). Com apenas um nível de interrupção utilizável, não existe maneira de a CPU permitir que um dispositivo de alta prioridade interrompa uma rotina de serviço de interrupção de média prioridade e ao mesmo tempo proibir um dispositivo de baixa prioridade de fazê-lo. Para resolver este problema, estas CPUs são normalmente utilizadas com um controlador de interrupção (ou árbitro de interrupção). Neste caso, quando a primeira interrupção acontece, digamos com prioridade n, a CPU é interrompida. Se uma interrupção subseqüente de maior prioridade acontecer, o árbitro interrompe pela segunda vez. Se a segunda interrupção tiver prioridade menor, ela é suspensa até a primeira terminar. (Quando terminar, a Todo o conteúdo desta seção foi retirado do livro Organização Estruturada de Computadores, de Tanenbaum, Ed. Prentice/Hall do Brasil. 8 54 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída rotina de interrupção deve enviar explicitamente um comando ao árbitro, para permitir que interrupções de prioridades mais baixas ocorram.) Figura 22 – Seqüência temporal de um exemplo de múltiplas interrupções. (RSI = Rotina de Serviço de Interrupção.) 3.2.1.4. Interrupções Mascaráveis e Não-Mascaráveis Suponha que um sistema possua um teclado alfanumérico para entrada de dados por parte do usuário. Como o teclado será utilizado em apenas alguns poucos instantes de tempo, uma boa saída seria utilizar interrupção para receber as teclas digitadas. Contudo, enquanto a CPU estiver ocupada, um usuário chato poderia estar teclando o teclado insistentemente sem nenhuma necessidade (acreditem, os chatos fazem parte de qualquer grupo de usuários). Neste caso, a tarefa da CPU estaria constantemente sendo interrompida para nada. Pensem como seria interessante poder inibir a interrupção do teclado. Para nossa alegria, isto é perfeitamente possível. Em qualquer sistema é possível inibir todas as interrupções ou apenas uma em específico. Na verdade, as interrupções estão inibidas por definição. Isto significa que, sempre que desejarmos trabalhar com interrupções, temos que habilita-las. O processo de inibir (ou mascarar) todas as interrupções geralmente é feito através do registrador de status da CPU. Por exemplo, como visto na Seção 2.5.1 (página 38), no 68HC11 isto é feito ligando-se o bit I do registrador CCR. Isto pode ser executado através da instrução SEI (Tabela 4). De forma semelhante, a instrução CEI habilita a ocorrência de interrupções desligando o bit I do registrador CCR. Como as interrupções estão inibidas por padrão, além de habilitar o reconhecimento de interrupções por parte da CPU, temos que habilitar a interrupção do dispositivo com o qual desejamos trabalhar. Por padrão, esta interrupção já pode estar habilitada, porém é bom verificar. Por último, se o sistema possui diferentes níveis de prioridades ou um árbitro de interrupção, cada nível pode ser inibido isoladamente. No seu computador, há duas formas de reinicia-lo: como as teclas CTRL + ALT + DEL ou com o botão RESET. Em ambos os casos, são utilizadas interrupções. Contudo, algumas vezes as teclas CTRL + ALT + DEL não funcionam. O mesmo não acontece com o botão RESET, ou seja, sempre podemos reiniciar o computador por este botão. Isto significa que, em momento algum a interrupção do botão RESET é inibida. Interrupções deste tipo são chamadas de interrupções não-mascaráveis, em oposição às interrupções que podem ser inibidas ou mascaradas. As interrupções não-mascaráveis têm prioridade sobre as mascaráveis. Além disso, elas são utilizadas para sinalizar “quase catástrofes”, por exemplo, um erro de paridade na memória. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 55 Curso de Microprocessadores 3.2.2. O Controlador 8259A O 8259A é um árbitro de interrupções utilizado com as primeiras CPUs da Intel (do 8080 ao 80286). Na verdade, o 80186 (uma versão melhorada do 8086, anterior ao 80286) já continha um arbitro de interrupção interno ao chip. Contudo, o 8259A continuou sendo utilizado pela Intel até o lançamento do 80386). Nos dias de hoje, o 8259A não é muito utilizado, porém ainda iremos estuda-los por dois motivos. Primeiro, o arbitro de interrupção dos computadores atuais possui uma programação compatível com o 8259A. (O entendimento desta programação pode ser útil, sobretudo, em sistemas de tempo-real9.) Segundo, ele irá nos ajudar a entender melhor a relação entre a CPU e os dispositivos de E/S. O 8259A gerencia até oito níveis de IRQs e possui suporte para expandir esta capacidade para até 64 níveis (utilizando outros 8259As em cascata). Ele é programável por software como um dispositivo de E/S qualquer. Além disso, uma seleção de modos de prioridade esta disponível ao programador, de tal forma que as IRQs são processadas de acordo com as necessidades do sistema. Estes modos podem ser alterados dinamicamente a qualquer instante. Figura 23 – Diagrama de blocos do 8259A. 3.2.2.1. Diagrama de Blocos O diagrama de blocos do 8259A é apresentado na Figura 23. Na Tabela 5, é apresentada a descrição de cada pino da Figura 23. Os dispositivos de E/S devem requisitar as interrupções através das entradas IR0 à IR7. Em qualquer instante, o estado desses pinos pode ser verificado através do registrador IRR (Interrupt Request Register, ou seja, Registrador de Requisição de Interrupção). Este registrador está conectado diretamente ao bloco responsável pelo arbítrio das IRQs, o Analisador de Prioridade (Priority Resolver). A IRQ de maior prioridade é selecionada e armazenada no registrador ISR (In Service Register, ou seja, Registrador de Interrupção em Serviço). A IRQ que passar pelo analisador de prioridade (apenas uma por vez) chega até a CPU através da saída INT. As que não passarem ficam registradas no IRR. O sinal IACK enviado pela CPU é recebido pela entrada INTA . O dispositivo ainda possui um registrador, o IMR (Interrupt Mask Register, ou seja, Registrador de Máscara de Interrupção), para inibir interrupções específicas. 9 Sistemas nos quais as tarefas devem ocorrer em instantes de tempo bem determinados. 56 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída Tabela 5 – Descrição dos pinos do 8259A. Os pinos CAS X e SP/EN são utilizados quando deseja-se expandir o número de linhas de interrupção. Eles serão abordados em um seção específica. Já as entradas RD , WR e CS são as nossas velhas conhecidas. A novidade é o pino A0 que, junto com RD e WR , são utilizados para enviar comandos ao 8259A, bem como ler seus registradores. Na verdade, A0 pode ser conectado diretamente às linhas de endereço do barramento. Finalmente, D7 – D0 são utilizadas como pinos bidirecionais para termos acesso à todos os registradores do controlador. 3.2.2.2. Seqüência de Interrupção A grande vantagem do 8259A, além da possibilidade de programação, é a capacidade de endereçar as rotinas de interrupção. Isto permite saltar para a rotina de interrupção correta sem a necessidade de verificar todos os dispositivos de E/S do sistema. A seqüência normal de eventos durante uma interrupção é apresentada a seguir. No 8080/85 os passos são os seguintes: 1. Uma ou mais linhas de interrupção (IR0 – IR7) são ativadas, ligando o bit correspondente do IRR. 2. 8259A avalia estas requisições no analisador de prioridades e envia a requisição à CPU (através do pino INT), se for o caso. 3. A CPU responde com um pulso no pino INTA . 4. Um bit do ISR é ligado e um no IRR é desligado (indicando qual requisição está sendo atendida). O 8259A também libera o opcode da instrução CALL (11001101b) nas linhas D7 – D0. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 57 Curso de Microprocessadores 5. Neste passo, após receber o opcode da instrução CALL, irá pulsar INTA mais duas vezes. 6. Estes dois pulsos dizem ao 8259A para colocar o endereço de 16 bits da rotina de interrupção no barramento (primeiro o LSB, depois o MSB). 7. No modo AEOI (Automatic End of Interrupt, ou seja, Fim de Interrupção Automático), o bit do ISR é desligado após o terceiro pulso de INTA . Caso contrário (o modo padrão), um comando EOI (constante $20) deve ser enviado. Os eventos que ocorrem nos sistemas baseados no 8086/88/286 são os mesmos até o passo 4. 4. Nada ocorre durante este passo. 5. Neste passo, o 8086/88/286, irá pulsar INTA uma única vez. Durante este pulso, um bit do ISR é ligado e um no IRR é desligado. Então, o 8259A põe um inteiro no barramento de dados da CPU. A CPU irá utilizar este inteiro para endereçar o vetor de interrupção. 3.2.2.3. Interfaceamento Quando utilizamos apenas um árbitro, a maneira correta de interfacear o 8259A como a CPU é apresentada na Figura 24. Aqui, desde que o 8259A está trabalhando como mestre, SP/EN deve ser colocado em Vcc. Fora isto, acredito que nesta altura, tudo deve parecer bastante óbvio. Figura 24 – Interface padrão do 8259A com o barramento. Para expandir a capacidade para 64 níveis de prioridades, o 8259A pode facilmente ser interconectado em um sistema com um mestre e oito escravos, como é apresentado na Figura 25. Nesta configuração, as saídas INTs dos escravos são conectadas às entradas “IR0 – IR7” do mestre. Assim, quando um escravo ativar INT, o mestre libera o escravo correspondente, após o primeiro pulso de INTA , colocando seu endereço nas linhas “CAS X”. Desse modo, estas linhas atuam como pinos de seleção de chip (chip select) durante os outros pulsos de INTA . O endereço de cada escravo é dado durante a inicialização do sistema, através do envio de comandos específicos à cada 8259A. Após a interrupção ter sido atendida, necessitamos enviar dois comandos de EOI (um para o escravo e outro para o mestre). Para seu próprio bem, não se esqueça disto, pois este é um daqueles erros que consomem um dia inteiro para ser descoberto. 58 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída Figura 25 – O 8259A utilizado em cascata. 3.2.2.4. Programação O 8259A aceita dois tipos de comandos: 1. Comandos de Inicialização (Initialization Command Words, ICWs) - Antes da sua operação normal poder ser iniciada, cada 8259A do sistema deve ser inicializado. Esta inicialização é feita enviadose uma seqüência de dois ou quatro bytes ao 8259A. Durante esta inicialização dizemos com qual CPU ele está trabalhando (8080/85 ou 8086/88/286), se ele é um mestre ou um escravo, se for escravo, qual o seu endereço (pinos CAS X, Seção 3.2.2.3), etc. Estes comandos devem ser enviados uma única vez. 2. Comandos de Operação (Operation Command Words, OCWs) - Com este comandos definimos o modo de operação dos controladores. Basicamente, existem quatro modos de operação: Fully Nested (padrão), Rotating Priority, Special Mask e Polled. Em cada um destes modos a prioridade dos dispositivos ligados ao 8259A pode mudar radicalmente. Só para dar uma idéia, no modo polled o sistema funciona como na técnica de E/S programada. [Comandos de operação podem ser enviados em qualquer instante de tempo (após a inicialização)]. Devido à grande capacidade de programação, a configuração do 8259A é um pouco complexa. Como não iremos utiliza-los em nossas experiências, encerraremos o assunto por aqui. Contudo, caso haja interesse (ou necessidade), aconselho os manuais 82C59A CMOS Priority Interrupt Controller e 82C59A Priority Interrupt Controller, ambos podem ser conseguidos gratuitamente no site “Harris Semiconductor – Search (http://www.semi.harris.com/search/index.htm)”. 3.2.3. Interrupções no 68HC11 Uma das grandes vantagens do HC11, em relação ao BASIC Stamp II, é o suporte a técnica de E/S por interrupção. Á primeira vista, esta técnica pode parecer um pouco confusa, porém, com um pouco de prática, veremos que ela facilita muito as coisas (Isto ficará claro na montagem do Experimento 9). 3.2.3.1. O vetor de interrupção O 68HC11 possui um vetor de interrupção de 18 posições que suportam 22 fontes de interrupção diferentes. Dessas, 15 são mascaráveis e são geradas pelos dispositivos internos ao microcontrolador. O vetor de interrupção do 68HC11 localiza-se entre as posições de memória FFC0h e FFFFh. A Tabela 6 informa a posição do vetor de interrupção de cada fonte. Não vamos discutir agora todas as fontes de Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 59 Curso de Microprocessadores interrupção da Tabela 6. Vamos analisar apenas as relacionadas aos pinos IRQ e XIRQ , e a do Timer Overflow (TOI). Tabela 6 - Fonte de interrupção, vetor de interrupção e a sua máscara. 3.2.3.2. Interrupção IRQ e XIRQ A interrupção XIRQ é não-mascarável. Por isso, ela deve ser utilizada apenas para sinalizar um falha grave no sistema. Quando o sistema é inicializado [após o RESET (outra interrupção nãomascarável)], a interrupção XIRQ está desabilidade. Logo, para que ela possa ser utilizada, deve-se limpar o bit X do registrador CCR. Contudo, uma vez habilitada, a interrupção XIRQ não pode ser inibida (de forma alguma). Além disso, é muito importante lembrar que apenas a instrução TAP pode limpar o bi X do registrador CCR. (Por favor, não se esqueça disso.) É importante notar que, quando a CPU reconhece uma interrupção mascarável, o bit I do registrado CCR é setado, ou seja, as interrupções mascaráveis são desabilitadas. Contudo, isto não afeta o bit X do CCR, ou seja, a interrupção XIRQ continua habilitada e pode interromper qualquer interrupção mascarável. Isto também quer dizer que, caso seja necessário uma interrupção mascarável interromper outra mascarável (com menor prioridade), o bit I do CCR deve ser limpo no início de todas rotinas de interrupção mascaráveis. Evidentemente, ao final do processo de interrupção (quando o registrador CCR é desempilhado) o bit I do registrador CCR é automaticamente limpo. O processo é ligeiramente diferente quando a CPU reconhece uma interrupção XIRQ . Neste caso, os bits X e I do registrado CCR são setados, ou seja, as interrupções mascaráveis e a XIRQ são desabilitadas. Como no caso anterior, ao final do processo de interrupção (quando o registrador CCR é desempilhado) os bits X e I do registrador CCR são automaticamente limpos. 60 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída O pino IRQ da CPU 68HC11 possibilita uma maneira de periféricos externos requisitarem interrupção à CPU. Trata-se de uma interrupção mascarável sensível a nível. Após o RESET, o pino IRQ é configurado para ser ativo em nível baixo. Contudo, como mostra a Figura 26, isto pode ser modificado através do bit 5 do registrador OPTION. Porém, como se trata do registrador que controla vários aspectos importantes da CPU, o seu conteúdo só pode ser alterado nos primeiros 64 ciclos de instrução após o RESET. Figura 26 – Registrador OPTION. 3.2.3.3. Interrupção do Timer Overflow (TOI) O 68HC11, assim como a maioria dos microcontroladores, possui um timer interno. Um timer é um registrador que simplesmente conta o tempo indefinidamente. O timer do 68HC11, chamado de TCNT (endereço $100E), é de 16 bits. Por isso, ele consegue contar de $0000 à $FFFF. Sua contagem ocorre em uma freqüência 4 vezes menor que a do relógio do microcontrolador. Isto quer dizer que, no 68HC11 com clock de 8 MHz, o tempo entre uma contagem e outra é de 0,5 µs. Um fato interessante é que, sempre que ocorre uma transição de $FFFF para $0000 (um overflow), o bit 7 do registrador TFLG2 é setado (como mostra a Figura 27). Contudo, uma vez setado, ele só pode ser limpo via software. Além disso, os bits do registrador TFLG2 tem a peculiaridade de só serem limpos quando (pasmem!) tentamos seta-los. Explicando melhor, para limpar o bit 7 do TFLG2, devemos escrever o seguinte código: LDAA STAA #$80 TFLAG2 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 61 Curso de Microprocessadores Figura 27 – Registrador TFLG2. Podemos utilizar a indicação do bit 7 do registrador TFLG2 para saber quando ocorreu um overflow no TCNT. Isto pode ser útil, por exemplo, quando queremos contar um tempo muito maior que 1 ms. Caso seja necessário, pode-se habilitar uma interrupção para que ela ocorra sempre que o bit TOF do registrador TFLG2 for setado. Esta interrupção é a interrupção do TOI (Timer Overflow Interrupt). Ela deve ser habilitada através do bit TOI (bit 7) do registrador TMSK2 (como mostra a Figura 28). Observe que a interrupção só ocorre no instante em que o bit TOF é setado. Isto quer dizer que, antes da interrupção ocorrer pela primeira vez, devemos zerar o bit TOF. Além disso, caso seja necessário que a interrupção ocorra mais de uma vez, deve-se zerar o TOF dentro da rotina de interrupção. 3.2.3.4. Interrupções na placa EVB Na placa EVB, todos os vetores de interrupção já estão definidos, ou seja, eles já apontam para endereços específicos. Além disso, o vetor de interrupção está gravado em uma memória EPROM. Logo, não pode ser alterado. Com isso, seria desejável que conseguíssemos colocar nossas rotinas de interrupção nos endereços apontados pelo vetor de interrupção. Contudo, isto não é possível. O máximo que podemos fazer é colocar uma instrução JMP neste endereço. Evidentemente, este JMP deve apontar para a nossa rotina de interrupção. Conteúdo do vetor de interrupção da placa EVB é apresentado na Tabela 7. Figura 28 – Registrador TMSK2. 62 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída Tabela 7 – Conteúdo do vetor de interrupção da placa EVB. 3.2.4. Interrupções no IBM PC AT A alguns anos atrás, muito projetos envolvendo microprocessadores eram baseados na CPUs Intel. Os mais populares eram o 8085 e o 8088/86. Hoje, os microprocessadores Intel já não são tão populares para uso em sistemas microprocessados (ao contrário dos microcontroladores, como os das famílias 8051 e 8096, ambos da Intel). Contudo, devemos estudar o processo de interrupção nestas CPUs, pois a grande maioria dos microcomputadores existente no mercado utilizam-nas. E, como vimos nas seções anteriores, apesar de compreensão relativamente difícil, a técnica de interrupção otimiza bastante o código em assembler. Como veremos em breve, podemos estender esta facilidade para as linguagens de auto-nível. 3.2.4.1. O Vetor de Interrupção do IBM PC AT Em uma CPU da família 80x86 o vetor de interrupção está localizado nos primeiros 1.024 bytes da memória (isto está correto paras as CPUs até o 80286, pois, a partir do 80386, a Intel utiliza um registrador para apontar para o início do vetor de interrupção). O vetor comporta 256 endereços de 4 bytes. No IBM PC AT, algumas dessas posições são reservadas para apontar para rotinas que realizam funções específicas, por exemplo, dar refresh na memória dinâmica. Outras posições são reservadas para interrupções de software, e outras para interrupções de hardware, por exemplo, o disco rígido. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 63 Curso de Microprocessadores As interrupções de hardware são controladas por dois controladores 8259A. Os sinas de IRQ0 à IRQ7 (Figura 23) do 8259A mestre são mapeadas nas posições de 8 à 15 do vetor de interrupção. As IRQs do segundo controlador são numeradas de IRQ8 à IR15 e são mapeados nas posições de 112 (70h) à 119 (77h) do vetor de interrupção. Estas interrupções são associadas a periféricos específicos, como apresentado na Tabela 8. A IRQ2 de um dos 8259A foi utilizada para interfacear o segundo controlador, como apresentado na Figura 25. Isto garante mais oito níveis de interrupção. Em qualquer CPU, as interrupções também são utilizadas para tratar algumas eventos que possam perturbar o sistema. Por exemplo, caso o programa do usuário tente dividir algum número por zero, a CPU imediatamente para o processamento e provoca uma interrupção para tratar este situação. A interrupção, por sua vez, pode, por exemplo, exibir uma mensagem rude na tela do computador ou em um display de cristal líquido. Este tipo de interrupção, geralmente, recebe o nome de armadilhas ou traps. A principal diferença entre armadilhas e interrupções é o fato das armadilhas ocorrerem sempre no mesmo treco de programa (relacionadas a uma determinada instrução), porém nem todas as vezes que o trecho é executado (como no caso da instrução de divisão). Algumas posições do vetor de interrupção do PC AT são reservadas especificamente para tratar com este tipo de interrupção. As posições do vetor reservadas para interrupções de software podem ser utilizadas livremente pelo programador. Por exemplo, pode-se escrever um programa de forma que uma interrupção seja gerada sempre que um determinado trecho do programa seja executado. Por sinal, esta é a pricipal característica das interrupções de software, ou seja, elas ocorrem sempre no mesmo trecho do programa e todas as vezes que este trecho é executado. Interrupção Posição do Vetor Controlador IRQ0 08h Timer (18,2 vezes/s) IRQ1 09h Teclado Segundo Controlador de IRQ2 0Ah Interrupções IRQ3 0Bh COM2/COM4 IRQ4 0Ch COM1/COM3 Disponível (geralmente IRQ5 0Dh utilizada pela placa de som) IRQ6 0Eh Disco Flexível IRQ7 0Fh Porta Paralela (impressora) IRQ8 70h Real Time Clock IRQ9 71h Disponível IRQ10 72h Disponível IRQ11 73h Disponível IRQ12 74h Disponível IRQ13 75h Co-Processador Matemático IRQ14 76h Disco Rígido IRQ15 77h Disponível Tabela 8 – Canais de interrupção dos dois controladores 8259A do PC AT e suas posições no vetor de interrupção. 3.2.4.2. Trabalhando com Interrupção na Linguagem C Agora surge uma questão: “Como posso criar minhas próprias rotinas de interrupção?”. Uma rotina de interrupção é diferente de uma rotina qualquer. Lembre-se que, ao final de uma rotina comum, apenas o endereço de retorno da rotina (ou função) é retirado da pilha. Como na interrupção, o estado da CPU após da interrupção tem que ser o mesmo de antes da interrupção, o programa deve salvar todos os registradores da CPU na pilha do sistema antes de atender ao pedido de interrupção. Além disso, ao final 64 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída da rotina de interrupção, todos os registradores são restaurados da pilha. O trecho de código abaixo ilustra como deve ser uma rotina de interrupção em Linguagem C. Void interrupt nome_da_rotina() { ... // As instruções que fazem parte da sua rotina deve ser colocadas aqui outport(0x20, 0x20); // Envia a constante 0x20 para o dispositivo no endereço 0x20 } Merece alguma explicação? Então vamos a ela... a palavra chave interrupt diz ao compilador C que esta rotina salva os registradores na pilha e restaura-os antes de sair da rotina de interrupção. O código outport(0x20, 0x20); avisa ao 8259A do computador que a requisição de interrupção já foi atendida (Dúvidas? Consulte a Seção 3.2.2.2.). Dessa forma, outro pedido de interrupção pode, se necessário, ser feito. Uma vez escrita a função de tratamento de interrupção, o seu endereço deve ser posto no vetor de interrupção. No caso da função anterior, caso desejássemos colocar o seu endereço na posição $78 do vetor de interrupção, teríamos um código como segue: ... disable() velha_int78 = getvect(0x78); setvect(0x78, nome_da_rotina); enable(); ... // inibe todas as interrupções mascaráveis // salva o conteúdo da posição 0x78 do vetor na variável velha_int78 // põe o endereço da função “nome_da_rotina” na posição 0x78 // libera todas as interrupções mascaráveis Tenho certeza que o trecho de código anterior precisa de explicação. Vamos a ela... Primeiro, é muito perigoso alterar o conteúdo do vetor de interrupção, pois, se alguma interrupção ocorrer enquanto estamos fazendo isto, pode causar sérios problemas. Por isso, é um ótimo hábito desabilitar as interrupções antes de mexer no vetor. Evidentemente, quando terminarmos de altera-lo podemos tornar a habilita-las. É exatamente isto que fazem as instruções disable() e enable(). É claro que antes de colocarmos o endereço da nossa rotina no vetor de interrupção já existe um endereço lá. Outro bom hábito de programação é restaurar o endereço original antes de terminarmos nosso programa. Para isto, temos que salvar o endereço existente no vetor antes de alterá-lo. Isto é feito utilizando uma variável global. No trecho de programa anterior, utilizamos a variável velha_int78 para salvar este endereço. As funções getvect(0x78) e setvect(0x78, nome_da_rotina) lêem e escrevem na posição $78 do vetor de interrupção, respectivamente. Em um programa em C, antes de uma variável ser utilizada ela deve ser declarada. Em C, a forma de declarar uma variável que recebe um endereço de uma rotina de interrupção é muito interessante. A forma de declarar a variável anterior, velha_int78, é void interrupt (*velha_int78)(); // deve ser colocada no início do programa, fora da função main Como disse, ao final do programa deve-se restaurar o vetor de interrupção com o endereço salvo. (No caso anterior, salvo na variável velha_int78.) O Trecho do programa que faz esta tarefa é exibido a seguir. // o trecho de código a seguir deve ser colocado no fim da função main disable(); setvect(78, velha_int78); enable(); Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 65 Curso de Microprocessadores Algumas vezes desejamos roubar uma rotina de interrupção pré-existente. Por exemplo, quando digitamos uma tecla no computador, provocamos uma interrupção. Se desejarmos, podemos substituir a rotina que trata esta interrupção esta interrupção por uma nossa. Até aqui, não há nada demais. Usamos o procedimento visto até aqui. Contudo, vale a pena lembrar que toda rotina de interrupção executa um conjunto de passos indispensáveis para o funcionamento de computador. Por exemplo, a rotina de tratamento de interrupção do teclada realiza os seguites passos quando solicitada: 1. Recebe a tecla digitada pelo usuário; 2. Armazena o código da tecla no buffer do teclado; 3. Avisa ao controlador do teclado que a tecla já foi salva no bufffer, e; 4. Avisa ao 8259A que a interrupção do teclado já foi atendida. Caso qualquer um dos passos anteriores não seja executado, nosso computador corre perigo. Isto seguinifica que temos que garantir que os passos anteriores sejam realizados. Uma forma de garantir isto é chamando a rotina de interrupção original. Desde que tenhamos salvado o endereço da rotina original, está é uma tarefa fácil. Para o caso de estarmos trabalhando, por exemplo, com a interrupção de hardware $08, nossa função de interrupção poderia ser com segue. Void interrupt nova_int08() { ... // As instruções que fazem parte da sua rotina deve ser colocadas aqui velha_int08(); // chama rotina de interrupção 08 original } Note que agora não executamos a instrução outport(0x20, 0x20). Pois, isto é feito pela rotina original da interrupção $08. Por último, segue o corpo completo de um programa que trabalha com rotinas de interrupção em C. #include <dos.h> #include <conio.h> #include <stdio.h> void interrupt nova_int (); // Declara a rotina de interrupção void interrupt (*velha_int)(); // Declara a variável que guarda o endereço da rotina original void interrupt nova_int () // A sua rotina de interrupção { … // Aqui vão as suas instruções (velha_int)(); // Chama a interrupção original } void main(void) { clrscr(); disable(); velha_int = getvect(??); // escolha a posição do vetor de interrupção setvect(??, nova_int); // escolha a posição do vetor de interrupção enable(); … // coloque aqui o corpo do seu programa disable(); setvect(??, velha_int); enable(); // escolha a posição do vetor de interrupção } 66 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída Para facilitar a escrita de futuros programas, na Tabela 9 é apresentado o conteúdo de cada posição do vetor de interrupção. Faça bom proveito! Posição do Vetor Interrupção Controlador Armadilhas 00 - 01 (Traps) Interrupção de 02 Erro de Paridade Hardware Armadilhas 03 - 07 (Traps) 08h IRQ0 Timer (18,2 vezes/s) 09h IRQ1 Teclado Segundo Controlador de 0Ah IRQ2 Interrupções 0Bh IRQ3 COM2/COM4 0Ch IRQ4 COM1/COM3 Disponível (geralmente 0Dh IRQ5 utilizada pela placa de som) 0Eh IRQ6 Disco Flexível 0Fh IRQ7 Porta Paralela (impressora) Interrupções de 10 – 6F Software 70h IRQ8 Real Time Clock 71h IRQ9 Disponível 72h IRQ10 Disponível 73h IRQ11 Disponível 74h IRQ12 Disponível 75h IRQ13 Co-Processador Matemático 76h IRQ14 Disco Rígido 77h IRQ15 Disponível Interrupções de 78 - FF Software Tabela 9 – Descrição de cada uma das posições do vetor de interrupção do PC AT. 3.3 E/S via DMA O controlador de DMA (Acesso Direto à Memória) é um dispositivo útil e poderoso para transferir dados entre dispositivos de E/S, ou entre dispositivo de E/S e a memória. Esta transferência ocorre muito rapidamente porque uma peça de hardware dedicada transfere dados de uma parte do computador para outra em apenas um ou dois ciclos de E/S por dado transferido. O DMA também minimiza a latência em atender um dispositivo de E/S, pois um hardware dedicado responde mais rapidamente que interrupções, e o tempo de transferência é curto. Logo, a quantidade de memória temporária necessária nos dispositivos de E/S é reduzida (Por que?). Além disso, o DMA também diminui a carga da CPU, pois ela não tem que executar instrução alguma para transferir dados. Portanto, o processador não é usado para gerenciar a transmissão e fica disponível para outras atividades. Isto é ainda mais importante em sistemas nos quais o microprocessador opera primariamente na sua memória cache. Neste caso, a transferência ocorre em paralelo, logo a performance geral do sistema é melhorada. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 67 Curso de Microprocessadores 3.3.1. Como o DMA funciona Um controlador de DMA gerencia vários canais de DMA. Cada canal pode ser programado para realizar uma seqüência de transferências. Dispositivos, normalmente periféricos de E/S que necessitam enviar ou receber dados, sinalizam para o controlador de DMA enviando um sinal de requisição de DMA (DRQX, com X igual ao número do canal). Um sinal de DRQX para cada canal é roteado para o controlador. Este sinal é monitorado e respondido da mesma forma que o processador gerencia interrupções. Quando o controlador de DMA recebe o sinal de requisição de DMA (DRQX), o controlador responde realizando uma ou mais transferências do dispositivo de E/S para a memória ou vice versa. Os canais do DMA precisam ser habilitados pelo processador para que o controlador de DMA responda aos sinais de DRQX. O número de operações efetuadas, modos de transferências usados, e locações de memória possíveis dependem de como os canais de DMA são programados. Um controlador de DMA, tipicamente, compartilha a memória do sistema com a CPU e pode operar como mestre ou escravo. Operando como mestre, o controlador assume o comando do barramento do sistema (linhas de endereço, dados e controle) para realizar as transferências. Operando como escravo, o controlador de DMA é acessado pela CPU, que programa os registradores internos ao controlador para configurar a transferência. Estes registradores consistem de registradores de endereço fonte e destino e contador de transferências, para cada canal de DMA. Além de um registrador de status para configuração e monitoramento da operação do controlador. 3.3.2. Tipos e modos de transferências Controladores de DMA variam de acordo com os tipos de transferências e modo suportados. Os dois tipos de transferência são o flyby e o fetch-and-deposit. Os três modos mais comuns são o sigle, block, e demand. Todos esses tipos e modos são descritos a seguir. O tipo de transferência mais rápida é o flyby. Nesta, uma única operação de barramento é usada para a transferência. Os dados são lidos da fonte e escritos no destino simultaneamente. Durante a transferência, o dispositivo envia um sinal de DRQX para o canal apropriado. Em seguida, o controlador toma o controle do barramento e envia um sinal para o dispositivo (sinal DACKX, com X igual ao número do canal). Este sinal avisa ao dispositivo para ler o dado do barramento ou coloca-lo no barramento, dependendo da direção da transferência. Em outras palavras, este tipo de transferência ocorre como uma operação de Leitura/Escrita na memória, apenas um ciclo de memória é utilizado. Apesar de muito eficiente, este tipo de operação não pode transferir dados da memória para a memória. O segundo tipo de transferência é chamado de fetch-and-deposit. Neste, são envolvidos dois ciclos de memória ou de E/S. Os dados são inicialmente lidos do dispositivo ou da memória e são armazenados em registradores internos ao controlador de DMA. Os dados são então escritos na memória ou no dispositivo de E/S no próximo ciclo. Apesar de ineficiente, pois utilizada dois ciclos de memória, este tipo de transferência é útil para transferir-se dados entre dispositivos incompatíveis. Por exemplo, um controlador de DMA pode efetuar duas leituras de 16 bits em um local, seguida de uma escrita de 32 bits. Diferente do tipo flyby, o fetch-and-deposit pode ser usado para operações entre a memória e a memória. Além dos tipos de transferência, um controlador de DMA pode suportar um ou mais modos de transferência. Os mais comuns são o sigle, block, e demand. O modo sigle é o mais lento, pois, neste tipo, o DMA transfere um único dado para cada sinal de DRQX. Isto pode não ser problema para sistema com pouca demanda de barramento, porém pode causar sérios problemas de latência quando múltiplos dispositivos tentam acessar o barramento. Os modos block e demand podem ser mais eficientes, pois permitem realizar várias transferências quando o DMA ganha o controle do barramento. No modo block, em resposta a um único sinal de DRQ, o DMA realiza múltiplas transferências de acordo com seu registrador contador. No modo demand, o DMA realiza transferências enquanto o dispositivo sustentar o sinal de DRQ. 68 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída 3.3.3. Operação do controlador Para cada canal, o controlador de DMA armazena os endereços dos dispositivos (ou posições de memória) envolvidos na transferência, além do número de transferências a serem realizadas (o contador). Esses dados são armazenados em registradores chamados “registradores de base”. Uma cópia dos contadores de base é armazenada nos registradores de endereço corrente e de contador corrente. Cada canal é habilitado ou inibido através de um registrador de máscara. Um canal de DMA é iniciado escrevendo-se nos registradores de base e habilitando-se o canal. A cada transferência, o valor no registrador de endereço corrente é colocado no barramento. Em seguida, o registrador é incrementado ou decrementado. O contador corrente determina o número de transferências remanescentes e é automaticamente decrementado a cada transferência. Quando o valor deste registrador passa de 0 (zero) para –1 (menos um), um sinal chamado terminal count (TC) é gerado. Isto significa que o DMA completou a seqüência de transferências. Este sinal pode ser monitorado pelos dispositivos de E/S que participam da transferência. O controlador de DMA necessita de programação a cada sinal TC. Quando este sinal ocorre, a CPU precisa programar o DMA para uma nova transferência. Para isto, alguns controladores de DMA interrompem a CPU à cada fim de transferência. Logo, o controlador consome algum tempo da CPU, mas muito menos que o consumido por serviços de E/S baseados em interrupções. Alguns controladores possuem mecanismos de auto-configuração. Geralmente, quanto mais sofisticado for o controlador, menos tempo da CPU ele vai consumir para realizar esta configuração. Um controlador de DMA também tem um ou mais registradores de status que são lidos pela CPU para determinar o estado de cada canal. Este registrador geralmente indica quando um canal foi requisitado e quando o canal envia um TC. Contudo, ler um registrador de status sempre elimina a informação do TC do registrador, o que pode provocar problemas se múltiplos dispositivos estão tentando usar canais diferentes. 3.3.4. Implementação de DMA no IBM PC e no PC/XT O controlador de DMA usado no IBM PC, PC/XT e na maioria dos microcomputadores é o 8237A, da Intel. Este controlador tem quatro canais, cada um dos quais possui um registrador de endereço e um registrador contador (ambos de 16 bits). O 8237A implementa os modos sigle, block e demand. O PC utiliza dois canais de DMA, um para transferir dados da unidade de discos flexíveis (canal 2) e outro para efetuar refresh na memória. (canal 0). Os canais remanescentes estão disponíveis para cartões de E/S. O 8237A do PC realiza transferência do tipo flyby de 8 bits, pois o barramento do PC é de 8 bits, além de um tipo especial de transferência de uma locação de memória para a outra. No PC, o 8237A opera a 4,77 MHz e pode transferir dados acima de 900 kbytes/s no modo demand. 3.3.5. Implementação de DMA no PC AT O PC AT aumentou o número de canais de DMA disponíveis para sete. Dois 8237A são usados em cascata. O controlador 1 possui os canais de 0 a 3 e implementa transferência do tipo flyby entre dispositivos de E/S de 8 bits e locações de 8 ou 16 bits da memória do sistema. O controlador 2 possui os canais de 4 a 7. O canal 4 é usado para interligar as operações dos dois controladores e portanto não está disponível para uso. Os canais 5, 6 e 7 implementam transferências flyby de 16 bits entre dispositivos de 16 bits e locações de memória de 16 bits. O registrador contador deste controlador é de 16 bits. As linhas de endereço A1 até A16 são armazenadas nos registradores de endereço, um registrador de página retém os sete bits mais significativos do endereço de 24 bits, enquanto que A0 é suposto ser 0. Logo, para transferências de 16 bits, o comprimento máximo da seqüência de transmissão é 128 kbytes e o registrador de página não é incrementado automaticamente. No AT, o 8237A opera a 4 MHz (seu limite máximo é de 5 MHZ) que permite, no modo demand, uma taxa de 800 kbytes/s para 8 bits e 1,6 Mbytes/s para 16 bits. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 69 Curso de Microprocessadores Tanto no PC original como no AT, o limite de transferência via DMA é limitado pelas necessidades e refresh de memória. Um ciclo de refresh é executado a cada 15 µs. Por isso, um dispositivo que utilize o modo demand tem de interromper o sinal de DRQ e liberar o barramento a cada 15 µs. Além disso, o modo block não é muito viável neste tipo de sistema, pois apenas 24 transferências podem ser efetuadas em 15 µs. 3.3.6. Gerenciando o controlador de DMA O DMA é um recurso compartilhado e pode ser usado por aplicações completamente diferentes, logo precisa ser propriamente gerenciado. As maiores preocupações são manter as informações de status coerentes e controlar a transferência dos dados, visto que não existe suporte por parte do sistema operacional para operações com DMA. O 8237A possui registradores de programação de 16 bits, porém são programados através de uma porta de 8 bits. Para isto, existe um registrador apontador que define quais das duas posições receberá cada operações de 8 bits de leitura ou escrita. Evidentemente, para evitar conflitos, o registrador apontador tem que ser limpo e as interrupções têm que ser interrompidas antes de cada operação de leitura ou escrita. Outro problema envolve a determinação de fim de transferência do DMA. Cada canal de DMA tem um bit de TC no registrador de status, infelizmente uma leitura neste registrador limpa o bit de TC de todos os canais. Logo, o dispositivo de E/S tem que achar outra forma de saber o fim de transferência de DMA. No caso do PC e do AT, isto é feito através de um dispositivo de E/S que trabalha como um “DMA slave” e gera uma IRQ para cada canal de DMA. 3.4 Interface de Comunicação Serial Devido à simplicidade do hardware, a comunicação serial é muito utilizada dentro da indústria eletro-eletrônica. Hoje, o padrão de comunicação mais utilizado é o EIA/TIA – 232 ou, simplesmente, RS232 (Recommended Standard). 3.4.1. Especificações RS-232 é um padrão completo, ou seja, ele especifica 1) características elétricas, 2) sinais de controle e handshake, e 3) características mecânicas. Cada uma dessas três características são discutidas a seguir. 3.4.1.1. Características Elétricas As especificações elétricas do padrão definem características como: níveis de tensão, slew rate e impedância da linha de transmissão. Desses, o mais importante para nós é a especificação dos níveis de tensão. Esta especificação foi definida antes da popularização da família TTL. Por isso, o padrão define níveis de tensão incompatíveis com esta família. O nível alto na transmissão foi definido de +5 a +15 volts. Já o nível baixo foi definido de –5 a –15 volts. Na recepção, o nível alto foi definido de +3 a +15 volts e o nível baixo de –3 a –15 volts. A Figura ??? ilustra estes níveis de tensão. Como última informação, saiba que, na comunicação RS-232, o nível baixo é definido como nível lógico 1 e o nível alto como nível lógico 0. 3.4.1.2. Sinais de Controle e Handshake O padrão RS-232 definiu a função de cada um dos sinais envolvidos na sua interface. Estes sinais são divididos em 4 categorias: comum, dados, controle, e temporização. A Tabela ??? apresenta todos os sinais definidos pelo padrão. Como se vê, existe um número exagerado de sinais. Felizmente, nenhuma aplicação utiliza todos os sinais. Por exemplo, um dos dispositivos que utilizam o padrão RS-232 é o modem. Este dispositivo utiliza tipicamente apenas oito sinais. Uma aplicação padrão pode funcionar basicamente com cinco desses sinais (1 comum, 2 de dados, e 2 de handshake). Em alguns casos, apenas três sinais podem ser utilizados (1 comum e 2 de dados). 70 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 3- Entrada e Saída 3.4.1.3. Características Mecânicas Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 71 Curso de Microprocessadores Capítulo 4 - Conversores A/D e D/A Conversão digital para analógico (D/A) é o processo de converter códigos digitais em uma faixa contínua de níveis de sinal analógico. Conversão analógico para digital (A/D) é o processo de converter uma faixa contínua de níveis de sinal analógico em códigos digitais. Tais processos de conversão são necessários para conectar os sistemas da natureza, que são tipicamente expressos por sinais analógicos contínuos variantes no tempo, com os sistemas digitais que processam, armazenam, interpretam, e manipulam as grandezas analógicos. O avanço na tecnologia de conversores A/D e D/A, assim como a popularização dos microcomputadores, fez com que muitos sistemas implementados de maneira analógica fossem convertidos para circuitos digitais. Pois, além de ter um custo reduzido (quando comparado à circuitos analógicos) estes sistemas apresentam a vantagem de serem programáveis. Além disso, alguns algoritmos são simplesmente irrealizáveis na forma analógica. Com o aumento da popularização dos algoritmos digitais, surge uma necessidade de conhecer-se a base teórica necessária à interface desses sistemas discretos com o nosso mundo analógico. Este capítulo tenta suprir um pouco desta necessidade apresentado conceitos como “Aliasing”, “Teorema da Amostragem”, e “Freqüência de Nyquist”, de uma forma, esperançosamente, fácil de entender. 4.1 Discretização Sabemos que em um intervalo de tempo de 1 µs não ocorre variação de temperatura significativa. Por isso, não adianta observar a temperatura durante este período. Neste caso, poderíamos definir um intervalo de tempo mínimo durante o qual não observaríamos a variação na temperatura. Dependendo da aplicação, o intervalo poderia ser escolhido como sendo 1 min. Dessa forma, a temperatura seria considerada constante (ou mesmo inexistente) durante todo o período de 1 min após a última observação. Desta forma, a temperatura seria considerada uma grandeza discreta no tempo, ou seja, existiria apenas para períodos de tempo específicos. Na Figura 29 é apresentado (a) representação gráfica de um discretizador que transforma a grandeza analógica f(t) em um sinal discreto no tempo, (b) a representação matemática de um discretizador e (c) o gráfico da grandeza analógica (linha contínua) e do sinal discreto (pontos grossos). 4.2 Teorema da Amostragem O teorema da amostragem foi introduzido por Shannon em 1949, e impõe restrições em termos de componentes de freqüência de um sinal, f(t), a ser discretizado. Este teorema pode ser resumido como: Para representar de forma unívoca um sinal f(t), é necessário amostrar-se (discretizarse) este sinal a uma freqüência superior ao dobro da maior componente de freqüência de f(t). Ou seja, para amostrar-se um sinal cuja maior componente de freqüência é fC é necessário uma freqüência de amostragem de pelo menos 2fC. Esta freqüência, 2fC, também é conhecida como freqüência de Nyquist. O teorema de Shannon (ou teorema da Amostragem) pode ser aceito observando-se o processo de digitalização no domínio da freqüência (Figura 30). Considere o sinal analógico f(t) representado na Figura 30(a). Assuma que este sinal tenha a Transformada de Fourier, F(ω), esboçado na Figura 30(b). Baseado na Figura 29(b) e no teorema de Shannon, um discretizador de Nyquist (observe o intervalo de amostragem igual a 1 2 f c ) para o sinal f(t) seria como ilustrado na Figura 30(c) e teria uma Transformada de Fourier como apresentado na Figura 30(d). Com isto, para obter o sinal discretizado da Figura 30(e), teríamos que multiplicar o gráfico da Figura 30(a) pelo da Figura 30(c), ou seja, convoluir a transformada da Figura 30(b) com a da Figura 30(d), 72 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 4- Conversores A/D e D/A para obter o trasformada da Figura 30(f). Desse modo, a Figura 30(e) representa do sinal f(t) discretizado e a Figura 30(f) a Transformada de Fourier do mesmo sinal após a discretização. Figura 29 - (a) representação de um discretizador, (b) representação gráfica de um discretizador e (c) sinal analógico (linha contínua) e sinal discreto (pontos grossos) . Agora, se necessário, pode-se recuperar o sinal analógico f(t) [Figura 31(e)] a partir do sinal discreto f(n) [Figura 31(a)]. Para saber como, precisa-se observar a Transformada de Fourier de f(n), Fp(ω) [Figura 31(b)]. Da Figura 31(b), vê-se que para obter-se a transformada da Figura 31(f) (que corresponde ao sinal f(t)) precisa-se multiplicar a transformada da Figura 31(b) pela da Figura 31(d), ou seja, convoluir a função da Figura 31(a) com a da função sinc [Figura 31(c)], cuja transformada de Fourier é apresentada na Figura 31(d). Como exposto nos parágrafos anteriores, comprova-se graficamente que um sinal amostrado com a freqüência de Nyquist pode ser recuperado. Falta provar que o sinal amostrado a uma freqüência menor que a de Nyquist não pode ser mais recuperado. Isto pode ser feito observando-se a Figura 32. Nesta figura apresenta-se vários exemplos de Transformadas de Fourier de sinais discretos amostrados a freqüências menores que a de Nyquist. Comparando-se estes exemplos com o da Figura 30(f), vê-se que é impossível encontra-se uma Transformada de Fourier que, multiplicada por qualquer uma das transformadas da Figura 32, gere a transformada da Figura 31(e). Neste caso, diz-se que ocorreu o Fenômeno de Aliasing. Observando-se mais uma vez a Figura 31, nota-se que a Transformada da Figura 31(d) é na verdade a função de transferência de um filtro passa-baixas ideal. Contudo, na prática, é impossível obter-se um filtro com esta função de transferência. Pois, neste caso, teríamos um sistema não-causal. Na Figura 33, apresenta-se a função de transferência de um filtro real. Desta figura, vê-se que, caso efetue-se a digitalização a uma freqüência muito próxima a de Nyquist, ainda obtem-se o fenômeno de aliasing, ou seja, na prática, tem-se que utilizar uma freqüência um pouco maior que a de Nyquist para separar ainda mais os espectros da Figura 30(f). Neste caso, com um filtro real (com a função de transferência da Figura 33) pode extrair as componentes de freqüências mais altas, para obter-se o espectro da Figura 31(f). Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 73 Curso de Microprocessadores Figura 30 – (a) sinal analógico f(t), (b) Transformada de Fourier de f(t), F(ω), (c) representação gráfica de um discretizador de Nyquist, (d) Transformada de Fourier do discretizador de Nyquist, (e) sinal discreto no tempo e em amplitude e (f) Transformada de Fourier do sinal discreto no tempo e em amplitude. 74 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 4- Conversores A/D e D/A Figura 31 – (a) sinal discretizado, f(n), (b) Transformada de Fourier de f(n), (c) gráfico da função sinc, (d) Transformada de Fourier da função sinc, (e) sinal analógico f(t) e (f) Transformada de f(t). O fenômeno de aliasing pode ainda ocorrer durante a amostragem de sinais com largura de faixa infinita, ou seja, sinais cuja Transformada de Fourier ocupa toda a faixa de freqüência (todo o espectro). Neste caso, o problema não é a função de transferência do filtro passa-baixas, mas sim a largura de faixa infinita do sinal. Pois, segundo o Teorema de Shannon, seria necessário uma freqüência de amostragem infinita para digitalizar o sinal. Para contornar este problema, tem-se que efetuar um estudo no sinal que pretende-se digitalizar para descobrir qual a faixa de freqüência que realmente contribui para a sua forma (diz-se que encontra-se a faixa de freqüência na qual concentra-se a maior quantidade de energia do sinal). Quando a faixa de freqüência mais significante do sinal é encontrada, tem-se que aplicar um filtro passa-baixas (algumas vezes passa-faixa), cuja freqüência de corte é igual à largura de faixa encontrada, ao sinal antes de digitaliza-lo. Desse modo, a freqüência de Nyquist de um sinal com largura de faixa infinita é duas vezes a maior componente de freqüência do sinal que contribui de forma significativa pa a sua forma. 4.3 Quantização Como visto na Seção 4.1, o processo de discretização (ou amostragem) transforma o sinal analógico f(t) em um sinal discreto no tempo. Contudo, f(t) continua sendo contínuo em amplitude. Por exemplo, a temperatura, assim como a maioria das grandezas existentes na natureza, é analógica. Desta forma, para a temperatura variar de 29 para 30 OC, ela tem que passar por todos os valores intermediários. Porém, provavelmente não é interessante saber que a temperatura variou de 29,0000000000 para 29,0000000001 OC. Pois os reflexos dessa mudança são desprezíveis. Neste caso, dependendo da aplicação, podemos escolher a menor variação de temperatura relevante. Este valor, por exemplo, pode ser Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 75 Curso de Microprocessadores 0,5 OC. Dessa forma, seria relevante saber se a temperatura variou, como exemplo, de 29,0 para 29,5 OC. Contudo, qualquer valor entre estes dois extremos seria considerada uma variação desprezível e poderia ser considerada zero, ou seja, 29,1 OC ou mesmo 29,4 OC poderia ser considerado 29,0 OC. Isto quer dizer que estaríamos transformando a temperatura em uma grandeza discreta em amplitude, ou seja, uma grandeza que pode assumir apenas uma quantidade finita de valores entre dois valores quaisquer. Para o nosso exemplo, dentro do intervalo 25 OC e 30 OC a temperatura poderia assumir os valores 25,0; 25,5; 26,0; 26,5; 27,0; 27,5; 28,0; 28,5; 29,0; 29,5 e 30 OC. O processo descrito anteriormente chama-se quantização. Figura 32 – Diversos exemplos da Transformada de Fourier de um sinal amostrado a uma freqüência inferior a de Nyquist. Figura 33 – Esboço da função de transferência de um filtro passa-baixas real (apenas as componentes de freqüência positivas). Para entender o processo de quantização, observe o gráfico da Figura 34(a). Nesta figura, apresenta-se graficamente o processo de quantização da grandeza temperatura utilizando-se um quantizador de 7 níveis (incluindo o zero). Como apresentado no gráfico, para valores de temperatura entre 0 e 1 OC, considera-se a temperatura como sendo 0. De forma semelhante, para valores entre 1 e 2 76 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 4- Conversores A/D e D/A C, considera-se a temperatura como sendo 1 OC. O mesmo raciocínio pode ser aplicado a todos os outros níveis de quantização.Em particular, para valores de temperatura maiores que 7 OC, considera-se a temperatura como sendo 6 OC. Assim, o sinal discretizado no tempo está agora discretizado também em amplitude. Em todo processo de quantização comete-se um erro. Este erro diminui à medida que o número de níveis de quantização aumenta. Isto é bastante intuitivo, pois, à medida que aumenta-se o número de níveis de quantização, diminui-se o tamanho dos degraus da Figura 34(a). Fazendo com que a escada Figura 34(a) aproxime-se de uma rampa perfeita. 6 5 4 3 1 2 Níveis de Quantização 5 4 3 2 1 Níveis de Quantização 6 O 1 2 3 4 5 6 7 Temperatura em graus celsius (a) O C 1 2 3 4 5 6 7 Temperatura em graus celsius O C (b) Figura 34 – Quantização da temperatura em sete níveis de quantização. Observando-se novamente a Figura 34(a), nota-se que, quando o valor de temperatura aproxima-se da ponta de um degrau, por exemplo, em T um pouco menor que 3 OC, o erro de quantização é máximo e é igual a 1 OC. Deslocando-se os degraus da Figura 34(a) um pouco para a esquerda (exatamente 0,5 OC), tem-se o gráfico da Figura 34(b). Neste gráfico, o erro máximo cometido é de 0,5 OC. Por isso, no processo de quantização, o esquema da Figura 34(b) é mais utilizado, pois reduz o erro máximo pela metade. Generalizando, pode-se afirmar que o processo de quantização pode ser representado pela Figura 35. Nesta figura o processo de quantização é apresentado em termos do nível de quantização q. (Para o exemplo anterior, q seria igual a 1 OC.) Além disso, o erro máximo cometido no processo de quantização é 0,5q. Lembrando que a precisão do quantizador é dada em função do número de níveis de quantização. 4.4 Codificação Entendido o processo de quantização, torna-se fácil o conceito de codificação. Pois, o processo de codificação consiste em atribuir-se um número binário a cada um dos níveis de quantização. Assim, o número de bits utilizado na representação de cada nível de quantização depende do número de níveis. Por isso, o número de bits do codificador também é uma maneira de expressar a precisão do codificador e, conseqüentemente, do sinal digitalizado. O processo de codificação é ilustrado na Figura 36. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 77 Curso de Microprocessadores Figura 35 – Processo de quantização e o erro intrínseco ao processo. Figura 36 – Processo de quantização e codificação. 4.5 Recuperação do Sinal Codificado Caso seja necessário recuperar o sinal analógico codificados na forma de números binários, deve-se usar um conversor D/A. Este processo é realizado fazendo-se o processo inverso da codificação, ou seja, de acordo com a Figura 36, para um dado número binário, obtem-se um sinal de tensão igual ao nível de quantização correspondente. O sinal amostrado é discreto no tempo e em amplitude. Ele é algum como o sinal da Figura 37(a). Na recuperação do sinal original, não se tem informação sobre o sinal entre os instantes de tempo de duas aquisições. Por isso, o primeiro passo da recuperação deve ser tornar o sinal contínuo no tempo. Isto pode ser conseguindo mantendo-se na saída do conversor D/A o nível de quantização correspondente ao código binário da entrada do mesmo, durante o período que não se tem informação sobre o valor do sinal. Por isso, na saída do conversor D/A o sinal é parecido com o da Figura 37(b). Evidentemente, quanto maior for o número de bits utilizado na codificação do sinal, melhor será o sinal recuperado. 78 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 4- Conversores A/D e D/A Figura 37 – (a) sinal digitalizado, quantizado e codificado, (b) sinal na saída do conversor D/A e (c) sinal na saída do filtro "suavizador". Neste ponto, falta ainda transformar o sinal na saída do converosr D/A discreto em aplitude. Observando novamente a Figura 31, vê-se que os degraus da Figura 37(b) são causados por componentes de alta freqüência contidas no sinal digitalizado. Dessa forma, para obter-se o sinal original, tem-se que aplicar o sinal da Figura 37(b) ao filtro passa-baixas da Figura 31(b). Este filtro algumas vezes é chamado de Filtro "Suavizador" (Smoothing Filter). Finalmente, após o Smoothing Filter, o sinal f(t) é (quase) completamente recuperado. 4.6 Critérios de Performance Os fatores que mais contribuem para a performance dos conversores A/D e D/A são: resolução, velocidade e linearidade. 4.6.1. Resolução A resolução de um D/A é a menor variação de tensão possível sua na saída (analógica). No A/D, a resolução é a menor variação de voltagem que pode ser detectada pelo sistema e que produz uma variação no código digital. A resolução determina o número total de códigos digitais, ou níveis de quantização, que são reconhecidos ou produzidos pelo circuito conversor. A resolução de um D/A ou A/D é geralmente especificada em termos de bits no código digital. Um código de n bits permite 2n níveis de quantização, ou 2n-1 degraus dentro da faixa dinâmica10 do conversor, como é apresentado na Figura 38. Quando o número de bits aumenta, o tamanho dos degraus de quantização (Seção 4.3) diminui, logo a exatidão do sistema aumenta. A resolução do sistema pode ser especificada também em termos do "tamanho" dos degraus de quantização (Figura 35). Contudo, neste caso, a resolução deve ser especificada em termos da faixa dinâmica do conversor. Por exemplo, resolução de 5 mV para uma faixa dinâmica de 5 V. 4.6.2. Velocidade A velocidade de um conversor A/D ou D/A é determinada pelo tempo que ele leva para realizar a conversão. Para os conversores D/A, a velocidade é especificada em termos de tempo de ajuste (o tempo necessário para que a tensão na saída do D/A torne-se estável). Já para os conversores A/D, ela é especificada em termos de tempo de conversão (tempo necessário para que o A/D realize a conversão). Nos conversores D/A, o tempo de ajuste depende da faixa dinâmica do conversor e da transição do código binário, ou seja, leva mais tempo para obter-se uma saída estável em uma transição de 0000 para 1111, do que em uma de 1110 para 1111. Por isso, o tempo de ajuste é especificado nas condições apropriadas. Os conversores A/D têm um tempo de conversão mínimo que limita a velocidade na qual eles podem realizar conversões contínuas. A taxa de amostragem é o número de vezes por segundo que o sinal analógico pode ser convertido no código digital. Logo, um conversor com tempo de conversão de 1 µs pode 10 A grosso modo, a faixa dinâmica de um conversor pode ser entendida como a faixa de tensão que pode ser aplicada a entrada (A/D) ou gerada na saída (D/A) do mesmo. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 79 Curso de Microprocessadores converter um sinal a uma taxa de amostragem máxima de 1 MHz. Por isso, devido ao teorema de Shannon, a velocidade de um conversor A/D é um critério importantíssimo na escolha do tipo de conversor. Figura 38 – Conversão A/D com 4 bits de resolução. 4.6.3. Linearidade Em termos de conversores A/D e D/A, existem dois tipos de linearidades: linearidade integral e linearidade diferencial. A linearidade integral é definida em termos do desvio de uma linha reta da origem ao final da faixa dinâmica do conversor. Esta linearidade pode ser conhecida aplicando-se valores da linha reta em questão na entrada do conversor e observando a sua saída. Na Figura 39 é apresentado o resultado da conversão de uma linha reta por um conversor A/D e o desvio dos pontos amostrados em relação aos pontos da linha reta aplicada a sua entrada. A linearidade integral deve ser considerada o maior desvio encontrado, neste caso, 0,5 LSB (Figura 39). A linearidade diferencial é a linearidade entre transições de código. Ela pode ser entendida como a medida da monotonicidade do conversor. Um conversor é dito monotônico se um aumento nos seus valores de entrada resulta em um aumento correspondente na sua saída. A Figura 40 mostra a característica de um conversor A/D ideal e a de um A/D com um código faltando (linearidade diferencial de 1 LSB). 4.7 Tipos de Conversores D/A A grande maioria dos conversores D/A realiza a conversão atribuindo pesos de tensão (ou de corrente) a cada um dos dígitos do código binário. Dessa forma, a sua saída analógica é produzida somando-se a contribuição de tensão de todos os dígitos que compõem a palavra à sua entrada. Para ilustrar este fato, são analisados dois tipos muito comuns de conversores D/A. 4.7.1. Conversor D/A à Resistores Ponderados Baseado no que foi apresentado no parágrafo anterior, a configuração mais intuitiva de um conversor D/A é a apresentada na Figura 41. Neste figura, as entradas b0, b1 e b2 são os dígitos binários do código digital e podem assumir os valores Vr (nível lógico um) ou GND (nível lógico zero), dependendo das posições das chaves analógicas. Baseado no fato da soma das correntes que entram no nó N ser igual a soma das correntes que saem deste mesmo nó, a equação de V0 em função dos outros parâmetros do circuito pode ser conseguida como apresentado na (Eq. 1). 80 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 4- Conversores A/D e D/A Figura 39 – Linha reta aparente adquirida com um conversor A/D e o gráfico do desvio dos postos digitalizados em relação a linha reta real. Figura 40 – Característica ideal de um conversor A/D e característica de um A/D com uma linearidade diferencial pobre (com um código faltando em 120 µs). Vrbo − Vo 2(Vrb1 − Vo ) 4(Vrb2 − Vo ) Vo + + = R R R RL Vrbo 2Vrb1 4Vrb2 Vo 2Vo 4Vo Vo + + − − − = R R R R R R RL Vr (b0 + 2b1 + 4b2) = Vo + 7Vo = Vo R + 7 RL R RL R RL × R VrRL (bo + 2b1 + 4b2) Vo = R + 7 RL Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ (Eq. 1) 81 Curso de Microprocessadores Vr R b0 b1 R/2 N + RL Vo - R/4 b2 Figura 41 – Conversor D/A à resistores ponderados. Da (Eq. 1), vemos que as entradas b0, b1 e b2 contribuem com pesos diferentes (em potência de dois) para a saída V0. Exatamente como deve ser para um conversor D/A. Generalizando o circuito da Figura 41, chegamos a (Eq. 2). Desta equação vemos que a saída V0 sempre será atenuada. Contudo, isto pode ser facilmente contornado usando-se qualquer circuito para amplificar V0. A maior desvantagem deste circuito pode ser a grande variedade de valores de resistores. Por exemplo, para um D/A de 12 bits, teríamos deste o valor R até R/2.048. Isto tem dois inconvenientes: a) é muito difícil conseguir 12 resistores que satisfaçam exatamente a todos os valores necessários ao circuito (R, R/2, R/4, R/8, ..., R/2.048), e; b) o circuito apresenta valores de resistores bastante diferentes, por exemplo, para R = 204,8 kΩ (valor inexistente comercialmente), R/2.048 = 100 Ω. Esta grande faixa de valores torna o circuito muito susceptível a ruídos. Por estas duas razões, o conversor D/A a resistores ponderados não é muito utilizado. Vo = 4.7.2. ( RLVr bo + 2b1 + ... + 2 n−1 bn −=1 2 − 1 RL + R ( ) n ) (Eq. 2) Conversor D/A à Rede R-2R Todos os problemas do conversor D/A à resistores ponderados são solucionados com o conversor rede R-2R. Pois, este tipo de conversor utiliza apenas dois valores de resistores e sua configuração é extremamente simétrica. Na Figura 42a é apresentado um circuito conversor R-2R. Para calcular a saída V0 em função dos outros parâmetros do circuito pode-se usar o teorema da superposição (somar a contribuição individual de cada entrada b? na saída V0). Feito isto, encontramos bo b1 b2 Vr Vo = Vr + + ou Vo = (bo + 2b1 + 4b2) , 12 12 6 3 que é muito parecida com a (Eq. 2). (a) 2R 2R I1 2R b0 R (a) R 2 2 R ) (b R 2 2 R R R R 2 R 2 2 R 1 b 2 b 2R b0 R I2 + R 2 R 2 V 2 R o I 1 b 0 1 b 2 b 0 b 2R R 2 1 R 2R 2R + Vo - b2 b1 R R (b) I2 R R 2R b1 Vo 2R 2R b2 R2 R1 Figura 42 – (a) Conversor R-2R de 3 bits e (b) conversor R-2R com saída amplificada. Novamente, uma configuração como a da Figura 42b pode ser utilizada para eliminar o termo 1/12. Generalizando, podemos escrever para um circuito semelhante ao da Figura 42a, mas com n entradas 82 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 4- Conversores A/D e D/A V0 = ( ) 1 b0 + 2b1 + ... + 2 n−1 bn−1 . ( n −1) 3× 2 Finalizando, pode-se dizer que, por razões óbvias, este tipo de conversor D/A é o mais utilizado. Observe que o tempo de ajuste do circuito da Figura 42b depende apenas do tempo que as entradas b0, b1, etc, tornam-se estáveis e, como explicado anteriormente, da faixa dinâmica do conversor e da transição do código binário. Isto quer dizer que não é muito difícil fabricar-se conversores D/A extremamente rápidos. 4.8 Tipos de Conversores A/D Sinais analógicos podem ser convertidos para códigos digitais por vários métodos, incluindo integração, aproximações sucessivas, conversão paralela (ou flash), modulação delta, sigma-delta, etc. Duas das técnicas mais comuns são o processo de aproximações sucessivas e o tipo flash. Sistemas de áudio ou vídeo digitais de alta resolução requerem técnicas de conversão especiais. Exemplos dessas técnicas são PCM (Pulse Code Modulation), e conversão sigma-delta. PCM é uma técnica de codificação de voz usada não apenas por sistemas de áudio e vídeo, mas também por sistemas de comunicações. A técnica sigma-delta envolve conceitos complexos da área de processamento digital de sinais e processos estocásticos e está fora do escopo de um curso de graduação. 4.8.1. Paralelo ou Flash A Figura 43 apresenta o diagrama de blocos de um conversor A/D paralelo. O circuito consiste de uma rede de resistores de precisão, 2n-1 comparadores e um codificador de prioridade digital. A rede de resistores estabelece tenções de referência para cada um dos níveis de quantização. Cada um dos comparadores indica quando a tensão de entrada está acima ou abaixo da tensão threshold (limite) de cada um dos níveis de quantização do circuito. As saídas dos comparadores são as entradas do codificador de prioridade. Este codificador produz a saída do código digital que pode ser armazenada em um registrador. Este tipo de conversor é utilizado em aplicações que exigem altas velocidades, tais como processamento de vídeo e imagem. Conversores paralelos de oito bits tipicamente operam na faixa dos 100 MHz. Contudo, esta velocidade tem um preço. Um conversor de 8 bits deste tipo necessita de 255 comparadores. Isto torna conversores flash de 10 bits ou mais extremamente caros. Para contornar este problema alguns fabricantes utilizam a topologia de um conversor flash modificado. Na Figura 44 é apresentado um conversor flash modificado de 16 bits baseado em dois conversores flash de 8 bits. Na figura, é aplicado ao conversor uma tensão analógica de 1.000 mV. Como o primeiro conversor é de 8 bits e tem faixa dinâmica de 4.096 mV (assim como todos os outros conversores do circuito, Figura 44), ele produz o código binário correspondente ao número 62. Este número é aplicado a um conversor D/A que produz uma saída analógica de 992 mV, ou seja, um erro de 8 mV. Este erro é amplificado 28 vezes e aplicado ao segundo conversor A/D que produz o código binário correspondente ao número 128. Então, o código binário de 16 bits é formado através dos 8 bits do primeiro conversor A/D (MSB) e dos 8 bits do último conversor (LSB). Evidentemente, desprezando-se o tempo de ajuste do conversor D/A, o conversor de 16 bits é duas vezes mais lento que os de 8 bits. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 83 Curso de Microprocessadores Figura 43 – Diagrama de blocos de um conversor A/D Flash. 4.096 mV A/D 8 bits 1.000 mV 4.096 mV + + - 992 mV 1.000 mV/16 mV=62 MSB 00111110 D/A 8 bits 2.048 mV/16 mV=128 4.096 mV 8 mV G=2 8 2.048 mV A/D 8 bits LSB 10000000 Figura 44 - Conversor A/D de 16 bits baseado em dois conversores de 8 bits. 4.8.2. Aproximações Sucessivas Conversores A/D baseado em aproximações sucessivas é o que apresenta melhor relação custo benefício, pois requerem um mínimo de componentes e são muito rápidos (tipicamente, possuem tempo de conversão de 10 µs). Por isso, é um dos mais utilizados. (Por exemplo, no conversor A/D do 68HC11.) 84 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 4- Conversores A/D e D/A Na Figura 45 é apresentado o diagrama de blocos de um conversor A/D de aproximações sucessivas de n bits. Este conversor gera o código digital de n bits em n passos. O coração deste tipo de conversor é o bloco denominado SAR (Successive Approximation Register, ou seja, Registrador de Aproximações Sucessivas). Este bloco individualmente compara a tensão de entrada (Vi) com o ponto central de cada um dos n níveis de quantização, para determinar os n bits do código binário. Esta comparação é feita como segue: o bloco SAR determina se a entrada analógica está acima ou abaixo do ponto central e seta o bit do código digital de acordo, ou seja, se estiver acima o bit é ligado, caso contrário, o bit é desligado. O bloco SAR então passa para o próximo bit e compara a entrada com o próximo ponto central. Como o bloco SAR precisa realizar uma aproximação para cada bit do código digital, um código de n bits requer n aproximações. Figura 45 – Diagrama de blocos de um conversor A/D baseado em aproximações sucessivas. Para esclarecer melhor o funcionamento do bloco SAR, vamos ao seguinte exemplo. Suponha um conversor A/D de aproximações sucessivas de 8 bits. Neste caso, tanto o bloco SAR quanto o conversor D/A são de 8 bits. Além disso, considere que todo o circuito é alimentado com 5,12 V, ou seja, cada nível de quantização é de 20 mV. No início do processo, quando o SAR é resetado, a sua saída vai para 128 (ou 256 mV). Suponha ainda que Vi seja feito igual a 121,6 mV. Neste caso, como 256 mV é maior que 121,6 mV, o bit mais significativo (bit 7) é considerado 0 e o bloco SAR muda sua saída para o ponto central da faixa inferior (128 mV). Como 128 mV é maior 121,6, o bit 6 do valor convertido é considerado 0 e a saída do SAR muda para 64 mV (00100000b ou 32d). Agora, 64 mV é menor que 121,6 (tensão Vi). Por isso, o bit 5 é considerado 1 e a próximo ponto é o centro da faixa superior, ou seja, 96 mV. Com isso, o bit 4 é considerado 1 e o SAR muda para 112 mV. Nos três passos restantes a saída do SAR assume os valores 120, 124 e 122 mV. Fazendo b3=1, b2=1, b1=0 e b0=1. Logo, o resultado da conversão é 00111101b ou 61d ou ainda 122 mV. Note que, no exemplo anterior, o erro máximo que pode ser cometido é de 20 mV (ou 1 LSB). O que não está de acordo com o que foi discutido na Seção 4.3. Isto ocorre pelo fato do SAR não está comparando Vi com o centro de cada nível de quantização, mas sim com o extremo. Este fato é facilmente corrigido adicionando 1 mV (ou 0,5 LSB) à saída do conversor D/A. Ou ainda, subtraindo 1 mV (ou 0,5 LSB) da tensão de entrada (Vi) do conversor A/D. Este mesmo raciocínio pode ser aplicado ao conversor Flash. Intuitivamente, podemos fazer uma comparação entre a velocidade dos conversores D/A com a dos conversores A/D. Nos conversores D/A a conversão é feita quase que imediatamente. No entanto, nos conversores A/D o processo envolve vários estágios. Em particular, como dito anteriormente, um conversor de aproximações sucessivas de n bits realiza sua conversão em n estágios. Isto demonstra como é bem mais difícil construir-se um conversor A/D rápido que um D/A veloz. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 85 Curso de Microprocessadores Capítulo 5 - Projeto de Sistemas Microcontrolados Neste ponto do curso, você já entende a CPU por dentro e conhece os principais componentes de um sistema microcontrolado. Além disso, já está apto a desenvolver programas em assembler (relativamente) complexos. Por último, com o que foi apresentada no Capítulo 3, você já tem um bom entendimento de como a CPU “conversa” com o mundo exterior, através dos dispositivos de E/S. Neste capítulo, simplesmente, você vai ter mais detalhas de como esta “conversa” é realizada. Mais especificamente, você verá como conectar a CPU e os dispositivos de E/S fisicamente. Novamente, nosso alvo principal será o 68HC11. Contudo, como sempre, o que for aprendido aqui será facilmente adaptável à outros microcontroladores. Isto será demonstrado na Seção ???, quando fizermos um projeto com o microcontrolador 80C31 da Intel. 5.1 Principais Componentes de um Sistema Microprocessado Na Figura 3 (Página 11) é apresentado o diagrama de blocos de um sistema microprocessado. Desta figura vê-se que os principais componentes de um sistema microprocessado são: CPU, memória e dispositivos de E/S. Basicamente, o modo como a CPU é “interfaceada” com uma memória é muito parecido com a maneira de se conectar uma CPU a dispositivos de E/S. Por isso, para iniciar o estudo de projetos de sistemas microprocessados, inicialmente veremos como podemos conectar uma CPU e algumas pastilhas de memória. 5.1.1. Memória RAM Evidentemente, existe uma infinidade de tipos memória que podem ser conectadas a uma CPU. Contudo, na maioria das aplicações, a memória estática é mais utilizada. Por isso, nosso estudo irá começar por ela. Na Figura 46 é apresentada a pinagem física de uma pastilha de memória estática de 8kx8. Este tipo de memória é referenciada pelo número 6264. O prefixo “62” informa que o chip 6264 é uma memória RAM estática (as RAM’s dinâmicas possuem o prefixo “61”). E ainda, o sufixo “64” informa que a memória possui 8.192 posições de memória de 8 bits (8.192x8 = 8kx8 = 64k). Contudo, você pode encontrar memórias estáticas que tenham uma numeração diferente. Figura 46 – Pinagem física de uma chip de memória 6264. Acredito que a pinagem de um chip de memória não é novidade para você. Por isso, vou me ater apenas a alguns detalhes. Por exemplo, da Figura 46, vê-se que o chip possui dois pinos Chip Enable (pinos 20 e 26). Se desejado, pode-se deixar um desses pinos sempre ativos e utilizar-se apenas um deles como 86 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados seleção do chip, por exemplo, o E1 . Outro pino interessante é o G . Este pino, quando desativado, coloca os pinos de dados da memória em alta impedância. Por isso, sempre que desejarmos realizar alguma operação com a memória (uma leitura ou escrita), é necessário ativar este pino. Novamente, uma outra forma de se “trabalhar” é garantir que este pino esteja sempre ativo e colocar-se as linhas de dados em alta impedância com o um dos pinos Chip Enable. Preste atenção! O que eu disse no parágrafo anterior não é regra. Apenas pode ser utilizado na maioria dos projetos. Contudo, você pode se deparar com uma aplicação na qual você precise usar todos os pinos de controle da memória 6264. Isto também á válido para todos os outros chips discutidos neste capítulo. 5.1.2. Memória EPROM Você sempre precisará de uma memória ROM. Assim que o seu sistema é ligado, ele precisa de um programa para executar. Este programa é quem determina o que o seu sistema vai fazer. Por isso, ele precisa ser gravado em uma memória ROM. Alguns microcontroladores possuem ROM interna. Neste caso, o microcontrolador deve ser colocado em um gravador especial para que o programa seja gravado em sua ROM interna. Na Figura 47 é apresentado o gravador utilizado para programar os microcontroladores da família PIC da Microchip. Este gravador é conectado à porta serial de um microcomputador. Dessa forma, o programa pode ser transferido para um microcontrolador que esteja no soquete do gravador. Figura 47 – Gravador para a família de microcontroladores da Microchip. Muitos outros microcontroladores, como o 68HC11, não possuem ROM interna. Neste caso, um chip de memória ROM deve ser incluído no sistema para armazenar o programa. Qualquer tipo de ROM pode ser utilizado. Contudo, o uso de uma memória ROM re-gravável facilita a atualização do sistema. Em nossos exemplos será utilizada uma memória EPROM 2764. O prefixo “27” informa que o chip 2764 é uma memória EPROM. E ainda, o sufixo “64” tem a mesma origem do sufixo da 6264. Novamente, você pode encontrar memórias estáticas que tenham uma numeração diferente. Na Figura 48 é apresentado a pinagem física da EPROM 2764. Como pode-se ver, a pinagem é muito parecida com a da 6264. A entrada OE tem a mesma função do pino G da 6264. Novamente, ele pode ser deixado sempre ativo. Os pinos PGM e Vpp são utilizados apenas durante a gravação e podem ser deixados ambos em 5 V. Todos os outros pinos devem ser conhecidos para você. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 87 Curso de Microprocessadores Figura 48 – Pinagem do EPROM 2764. 5.1.3. Microcontrolador 68HC11 Na Figura 49(a) é apresentada a pinagem do 68HC11. Como o 68HC11 possui diversos dispositivos encapsulados em seu chip (conversor A/D, temporizador, comunicação serial, etc) ele possui uma quantidade elevada de pinos (52 no total). Contudo, para conectarmos memórias RAM ou EPROM e dispositivos de E/S, apenas os pinos da Figura 49(b) são relevantes. Dentre esses, os mais importantes são os pinos de dados e de endereço. É por esses pinos, que trafegam todas as informações do sistema microprocessado. No caso do 68HC11, ele é uma CPU de 8 bits que pode endereçar até 64 Kbytes de memória (incluindo memória RAM, ROM e dispositivos de E/S). Por isso, ele deve possuir oito pinos de dados e dezesseis pinos de endereços. Como dito anteriormente, na maioria dos microcontroladores, os pinos de dados e os pinos de endereços são multiplexados. O 68HC11 não é uma exceção à regra, pois os seus oito pinos de endereço menos significativos (A0-A7) são multiplexados com os seus oito pinos de dados (D0-D7). Para explicitar isto, esses oito pinos são numerados de AD0 à AD7. Os oito pinos de endereço mais significantes são numerados de A8-A15. Para entender como esta multiplexação é possível, basta observar os diagramas de tempo da Figura 50. Desta figura, fica claro que, tanto no ciclo de leitura como no ciclo de escrita, as linhas (pinos) de endereço são necessárias logo no início do ciclo, mas as linhas de dados são necessárias apenas um pouco depois. Por isso, no 68HC11 (e nas outras CPUs também ocorre algo semelhante), os pinos de AD0 à AD7 são pinos de endereço no início do ciclo e tornam-se pinos de dados um pouco depois. Com isso, é necessário encontrar uma forma de manter um endereço válido durante todo o ciclo de leitura/escrita. Para isto, normalmente, utiliza-se o esquema da Figura 51. Neste esquema, no início do ciclo, o 74HC373 é carregado com os oito bits de endereço menos significativos. Dessa forma, esses oito bits ficam acessíveis durante todo o ciclo. A carga do 74HC373 é controlada pelo pino AS (Figura 51). Da Figura 50, vê-se que este pino só é ativado, como desejado, no início do ciclo. De uma forma ou de outra, toda CPU provê um pino que tem um nome diferente, mas que possui a mesma função do AS. O uso do 74HC373 faz com que o sistema apresente três tipos de linhas: endereço, dados e controle. Como será visto mais adiante, todos os outros dispositivos do sistema são conectados nessas linhas. As seções seguintes, é apresentada a função de cada um dos pinos da Figura 49(b). 5.2 Interface entre o 68HC11 com Outros Dispositivos Fazer o 68HC11 “conversar” com outros dispositivos de entrada e saída não é uma tarefa difícil. Pare isto, basta ter em mente a Figura 51. Como dito na seção anterior, o esquema da Figura 51 deixa todos os sinais (endereço, dados e controle) do 68HC11 bem acessíveis. Dessa forma, basta conectar cada 88 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados um dos pinos do dispositivo que deseja-se “interfacear” com o 68HC11 aos barramentos da Figura 51. A Figura 52 apresenta as ligações necessárias para conectar uma memória 6264 com o 68HC11. (a) (b) (c) Figura 49 –(a) Todos os 52 pinos do 68HC11, (b) Os pinos mais importantes quando deseja-se interfacea-lo com dispositivos externos, e (c) Configuração sugerida para alguns desses pinos. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 89 Curso de Microprocessadores Endereço Endereço Dado Dado Escrita Leitura E AS E AS (a) (b) Figura 50 – (a) Diagrama de tempo de um ciclo de escrita típico no 68HC11; (b) Diagrama de tempo de um ciclo de leitura típico no 68HC11 Figura 51 – Barramento de um sistema baseado no 68HC11. O 74HC373 é utilizado para “separar” as linhas de endereço das linhas de dados. A Figura 52 não apresenta muita informação nova. Como dito, apenas conectou-se os pinos da 6264 aos barramentos do 68HC11. Contudo, algumas ligações valem à pena serem comentadas. Por exemplo, a 6264 é uma memória de 8 kbytes. Por isso, possui apenas treze linhas de endereço. Isto faz com que algumas linhas (A13, A14 e A15) do barramento de endereço do 68HC11 não sejam conectadas a nenhum pino da memória. Com isso, surge uma pergunta: “Qual é o endereço da primeira posição da memória 6264?”. Evidentemente, a primeira posição de memória é acessada quando todos os pinos de endereço da 6264 são zero. No circuito da Figura 52, isto acontece quando as linhas de A0 à A12 são zero, ou seja, 90 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados para acessar a primeira posição da memória (o seu endereço base) os pinos A13, A14 e A15 não são relevantes. Dessa forma, o endereço base da memória será acessado quando a constante $0000 (16 bits), $2000, $4000, $6000, $8000, $A000, $C000 ou $E000, for colocado nas linhas de endereço do 68HC11. Contudo, o endereço base não é acessado quando, por exemplo, se a constante $B000 é posta nas linhas de endereço, pois, neste caso, A12 é igual à “1”. Com isso, a posição acessada é a de número 2.048. 74HC373 30 29 XT EX AD0 AD1 AD2 AD3 AD4 AD5 AD6 AD7 AS 39 RESET A8 A9 A10 A11 A12 A13 A14 A15 E 41 40 IRQ R/W 31 32 33 34 35 36 37 38 3 4 7 8 13 14 17 18 26 11 16 15 14 13 12 11 10 9 27 1D 2D 3D 4D 5D 6D 7D 8D 1Q 2Q 3Q 4Q 5Q 6Q 7Q 8Q C OC 2 5 6 9 12 15 16 19 1 10 9 8 7 6 5 4 3 25 24 21 23 2 26 27 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 CS2 WE D0 D1 D2 D3 D4 D5 D6 D7 CS1 OE 11 12 13 15 16 17 18 19 20 22 MCM6264 28 XIRQ MODA MODB 25 24 68HC11A8 Figura 52 – Interface entre o 68HC11 e uma memória 6264. O fato de um dispositivo possuir mais de um endereço base não causa problema algum. Contudo, caso seja desejado que o dispositivo possua apenas um único endereço base, deve-se usar na seleção do mesmo todos os pinos que compõem o barramento de endereço. Explicando melhor, a memória 6264 possui dois pinos de seleção ( CS1 e CS2). Se for utilizada lógica adicional para fazer com que a memória 6264 seja accessível apenas quando, por exemplo, os pinos A13, A14 e A15 forem todos zero, o seu endereço base só poderá ser acessado quando a constante $0000 for colocada no barramento de endereço do 68HC11. Na Figura 53 é apresentada um esquema onde a memória 6264 possui endereço base $0000. Na figura, foram utilizadas duas portas lógicas OU para incluir os pinos A13, A14 e A15 na seleção do chip (pino CS1 ). Outro fato importante a ser observado quando se projeta sistemas microprocessados é que nem sempre a CPU quer acessar seus dispositivos externos. Por isso, o 68HC11 possui um pino de seleção externa (pino E). Enquanto este pino está desativado o 68HC11 faz alguma operação interna. Quando ele deseja acessar um de seus dispositivos externos, o pino E é ativado. Observando novamente os diagramas de tempo do 68HC11 (Figura 50), nota-se que o pino E só é ativado quando todos os outros sinais já estão estáveis. Para que o 68HC11 possa selecionar os dispositivos apenas quando desejar, o seu pino E deve ser conectado (de alguma forma) ao pino de seleção do dispositivo. Isto foi feito na Figura 52 e na Figura 53. Novamente, de uma forma ou de outra, outros microcontroladores têm pinos de nomes diferentes que desempenham a mesma função do pino E do 68HC11. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 91 Curso de Microprocessadores 74HC373 30 29 AD0 AD1 AD2 AD3 AD4 AD5 AD6 AD7 XT EX AS 39 A8 A9 A10 A11 A12 A13 A14 A15 RESET E 41 40 IRQ R/W 31 32 33 34 35 36 37 38 3 4 7 8 13 14 17 18 26 11 16 15 14 13 12 11 10 9 1D 2D 3D 4D 5D 6D 7D 8D 1Q 2Q 3Q 4Q 5Q 6Q 7Q 8Q C OC 2 5 6 9 12 15 16 19 10 9 8 7 6 5 4 3 25 24 21 23 2 1 20 26 1 3 2 1 3 2 27 27 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 CS1 CS2 WE D0 D1 D2 D3 D4 D5 D6 D7 OE 11 12 13 15 16 17 18 19 22 MCM6264 28 XIRQ MODA MODB 25 24 68HC11A8 Figura 53 - Interface entre o 68HC11 e uma memória 6264. Neste caso, a memória possui endereço base $0000. E se desejarmos acrescentar outro dispositivo ao sistema? Da mesma forma, basta conectar os pinos do novo dispositivo aos barramentos do 68HC11. Contudo, deve-se tomar o cuidado de fazer com que NUNCA os dois dispositivos sejam acessíveis ao mesmo tempo. Isto é possível se cada dispositivo contiver o seu próprio endereço base. Na Figura 54 é apresentado o mesmo sistema da Figura 53 acrescido de uma memória EPROM 2764. Foi utilizada uma porta lógica NAND de três entradas para fornecer o endereço base $E000 à memória EPROM. Com isso, os chips nunca são acessados ao mesmo tempo. 29 XT EX AS 39 A8 A9 A10 A11 A12 A13 A14 A15 RESET E 41 40 R/W IRQ 3 4 7 8 13 14 17 18 26 11 16 15 14 13 12 11 10 9 1Q 2Q 3Q 4Q 5Q 6Q 7Q 8Q C OC 10 9 8 7 6 5 4 3 25 24 21 23 2 1 3 2 1 3 2 27 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 CS1 CS2 WE MCM6264 D0 D1 D2 D3 D4 D5 D6 D7 OE 11 12 13 15 16 17 18 19 22 10 9 8 7 6 5 4 3 25 24 21 23 2 20 22 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 27 U3 2 5 6 9 12 15 16 19 20 26 1 27 O0 O1 O2 O3 O4 O5 O6 O7 11 12 13 15 16 17 18 19 CE OE 27C64 28 XIRQ MODA MODB 1D 2D 3D 4D 5D 6D 7D 8D VPP 30 31 32 33 34 35 36 37 38 PGM 74HC373 AD0 AD1 AD2 AD3 AD4 AD5 AD6 AD7 1 5V 25 24 1 2 13 12 68HC11A8 Figura 54 – Interface entre o 68HC11 e uma memória 6264 (base $0000) e uma memória EPROM 2764 ($E000). Evidentemente, à medida que a quantidade de dispositivos existentes no sistema aumenta, o uso de lógica adicional para fornecer endereços base únicos à cada dispositivo do sistema torna-se muito 92 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados complexa, cansativa e cara. Por isso, o mais eficiente é utilizar o decodificador 74HC138 para selecionar cada chip individualmente. Na Figura 55 é apresenta da um sistema que possui uma memória 6264 (endereço base $4000) e uma memória EPROM 2764 ($E000). Neste caso, foi utilizado o 74HC138 para atribuir os endereços base. Observa-se que os dispositivos conectados ao 74HC138 podem possuir apenas os seguintes endereços base: $0000, $2000, $4000, $6000, $8000, $A000, $C000 ou $E000. Cada dispositivo irá ocupar uma faixa de 8 kbytes de memória (independente de quantas posições de memória ou registradores ele possuir). Isto é ruim, sobretudo para dispositivos de E/S. Por exemplo, um dispositivo que possua três registradores internos (configuração, status e dados) tem apenas duas linhas de endereço. Neste caso, como ele possui apenas três posições de memória endereçáveis, estariam sendo desperdiçados 8.192 – 3 = 8.189 endereços. Contudo, como um pouco de lógica adicional, pode-se fazer com que dois (ou mais) dispositivos de E/S compartilhem o mesmo espaço de endereçamento de 8 kbytes. Desta forma, reduz-se o desperdício de espaços de endereçamento. No entanto, tenha em mente que, caso o seu sistema não precise de muitos espaços de endereçamento, ou seja, não possua muito dispositivos, você não precisa preocupar-se com os endereços desperdiçados. 29 AD0 AD1 AD2 AD3 AD4 AD5 AD6 AD7 XT EX AS 39 A8 A9 A10 A11 A12 A13 A14 A15 RESET E 41 40 R/W IRQ 26 11 16 15 14 13 12 11 10 9 27 28 XIRQ MODA MODB 1D 2D 3D 4D 5D 6D 7D 8D 1Q 2Q 3Q 4Q 5Q 6Q 7Q 8Q C OC 1 10 9 8 7 6 5 4 3 25 24 21 23 2 20 27 74HC138 1 2 3 6 5 4 A B C Y0 Y1 Y2 Y3 G1 Y4 Y5 G2BY6 G2AY7 15 14 13 12 11 10 9 7 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 CS1 WE MCM6264 D0 D1 D2 D3 D4 D5 D6 D7 CS2 OE 11 12 13 15 16 17 18 19 26 22 5V 10 9 8 7 6 5 4 3 25 24 21 23 2 20 22 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 27 U3 2 5 6 9 12 15 16 19 PGM 30 3 4 7 8 13 14 17 18 VPP 74HC373 31 32 33 34 35 36 37 38 1 5V O0 O1 O2 O3 O4 O5 O6 O7 11 12 13 15 16 17 18 19 CE OE 27C64 25 24 68HC11A8 Figura 55 - Interface entre o 68HC11 e uma memória 6264 (base $4000) e uma memória EPROM 2764 ($E000). Neste caso, foi utilizado o 74HC138 para atribuir os endereços base. Apesar do 74HC138 facilitar a definição dos endereços bases dos diferentes dispositivos do sistema, geralmente, os endereços dos dispositivos internos do microcontrolador não devem conflitar com os endereços dados aos dispositivos externos. Para evitar isto, o mapa de memória do microcontrolador deve ser utilizado. Na Figura 56 é apresentado o mapa de memória do 68HC11. Na figura, as áreas escuras são áreas ocupadas por dispositivos internos. Por exemplo, o endereço $0000 à $03FFF é ocupado com a memória RAM interna ao microcontrolador. Por isso, nenhum dispositivo externo pode ocupar esta faixa de memória. Em vista disso, os circuitos da Figura 52 e da Figura 53 estão errados, pois um dispositivo externo está ocupando esta faixa de memória. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 93 Curso de Microprocessadores 5.3 Cristal Oscilador Todas as CPUs necessitam de um relógio para funcionar. Como você sabe, diversas operações precisam ocorrer para que uma simples instrução seja executada. A seqüência dessas operações é comandada pelo relógio da CPU. Para fornecer este relógio, alguns projetistas utilizam um circuito RLC. Pessoalmente, não gosto desses osciladores. Pois, sua freqüência de oscilação varia muito com os valores dos componentes que, por sua vez, variam muito com parâmetros como, por exemplo, umidade e temperatura. Além disso, esses osciladores não conseguem oscilar a freqüências elevadas. Contudo, osciladores RLC são baratos. Por isso, alguns os utilizam. Figura 56 – Mapa de memória do 68HC11. Para obter-se precisão e freqüências elevadas, é necessário o uso de um oscilador a cristal. A Figura 57(a) apresenta o esquema de ligação de um oscilador a cristal. Como a figura mostra, além do cristal XTAL [ver Figura 57(a)], são necessários um resistor (Rf) e dois capacitores (C1 e C2). Os valores desses componentes dependem de variáveis como, por exemplo, freqüência do cristal, método empregado na sua fabricação e leiaute da placa de circuito impresso. Contudo, a dependência da freqüência de oscilação com esses parâmetros não é muito grande. Por isso, na maioria dos projetos, podem ser usados Rf = 10 MΩ e 5 pF < C1 = C2 < 30 pF. Em alguns casos, fazer a placa de circuito impresso que comportará o oscilador a cristal é um problema. Pois, o leiaute da placa deve evitar ao máximo as capacitâncias parasitas. Visto que, essas capacitâncias podem fazer com que o oscilador não oscile. Na Figura 57(b) é apresentado uma sugestão de leiaute. Na figura, a carcaça do cristal é conectada ao terra. Além disso, uma trila de GND passa entre os pinos dos capacitores. 94 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados (a) (b) Figura 57 – (a) esquema de ligação do cristal oscilador no 68HC11. (b) sugestão para confecção da placa de circuito impresso. 5.4 RESET Em qualquer sistema microprocessado pelo menos uma interrupção é utilizada: a interrupção do RESET. Esta interrupção deve ser ativada sempre que o sistema é ligado ou o usuário aperta um botão. Esta ativação ocorre através de um pino do microprocessador. Este pino, dependendo do microprocessador, pode ser ativo em nível alto ou baixo. A Figura 58 apresenta várias configurações para RESET. A saída Vo (Figura 58) deve ser conectada ao pino de reset do microprocessador (ou microcontrolador). Para os circuitos desta figura, os três circuitos de cima devem ser utilizados para resets ativos em nível alto. O primeiro circuito é o mais simples de todos. Simplesmente, Vo vai para 5 V momentaneamente quando o sistema é ligado ou quando o usuário aperta o botão da figura. O circuito do meio acrescenta um resistor de 1 kΩ para impedir que uma corrente que surja durante a descarga do capacitor danifique o microcontrolador. Já o circuito da direita, utiliza um inversor de histerese para limpar o sinal gerado pelo capacitor. Os circuitos na linha de baixa são os mesmos circuitos, só que para pinos de reset ativos em nível baixo. 5V 5V 5V 10 uF 10 uF 1k Vo 100 k 100 k 1 100 k 5V 2 Vo Vo 10 uF 5V 5V 100 k 100 k 10 uF Vo 10 uF 10 uF 1k Vo 1 2 Vo 100 k Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 95 Curso de Microprocessadores Figura 58 – Diversas configurações para RESET. 5.5 Projetos com o 80C31. O microcontrolador 80C51 é um dos mais populares. Assim como o 68HC11, o 80C51 é de oito bits e pode endereçar até 64 kbytes de memória. Além disso, ele possui uma ROM interna, na qual o programa que gerencia o sistema deve ser gravado. Uma alternativa mais barata que o 80C51 é o 80C31. O 80C31 totalmente compatível com o 80C51. A diferença é que o 80C31 não possui ROM interna. Por isso, o seu programa deve ser gravado em uma EPROM externa. A Figura 59 mostra a pinagem do 80C31. Nesta figura foram omitidos os pinos de comunicação serial (que não são utilizados na interface de outros dispositivos). 31 19 18 14 15 12 13 9 1 2 3 4 5 6 7 8 U1 EA/VP P0.0 P0.1 P0.2 P0.3 P0.4 P0.5 P0.6 P0.7 ALE/P X1 X2 T0 T1 P2.0 P2.1 P2.2 P2.3 P2.4 P2.5 P2.6 P2.7 INT0 INT1 RESET P1.0 P1.1 P1.2 P1.3 P1.4 P1.5 P1.6 P1.7 RD WR PSEN 39 38 37 36 35 34 33 32 30 21 22 23 24 25 26 27 28 17 16 29 80C31 Figura 59 – Pinagem do microcontrolador 80C31. Da Figura 59, é importante observar três fatos. Primeiro, o 80C31 não possui um pino E como o 68HC11. Além disso, a indicação de leitura ou gravação é indicada por pinos distintos ( W e R ). Quando a CPU esta fazendo uma operação interna, estes dois pinos estão em “1”. Quando a CPU deseja efetuar uma escrita ou uma leitura, o pino correspondente vai para “0”. Em nenhuma ocasião, os dois pinos vão para zero ao mesmo tempo. Por último, note que o 80C31 possui um pino PSEN (ativo em nível baixo). Este pino é ativado sempre que a CPU acessa a memória de programa. Por isso, ele deve ser utilizado para habilitar a memória EPROM onde está gravado o programa que controla o sistema. Na Figura 60 é apresentado um sistema baseado no 80C31. Como o 80C31 também possui pinos de dados e endereços multiplexados, o sistema é muito parecido com o sistema baseado no 68HC11. Na figura, a saída da porta lógica AND tem a mesma função do pino E do 68HC11. 19 12 MHz Y2 18 33 pF C2 5V C3 10 uF SW1 RST 100 k R1 14 15 12 13 9 1 2 3 4 5 6 7 8 X1 X2 T0 T1 INT0 INT1 RESET P1.0 P1.1 P1.2 P1.3 P1.4 P1.5 P1.6 P1.7 80C31 P0.0 P0.1 P0.2 P0.3 P0.4 P0.5 P0.6 P0.7 ALE/P P2.0 P2.1 P2.2 P2.3 P2.4 P2.5 P2.6 P2.7 RD WR PSEN 39 38 37 36 35 34 33 32 30 3 4 7 8 13 14 17 18 11 21 22 23 24 25 26 27 28 1D 2D 3D 4D 5D 6D 7D 8D 1Q 2Q 3Q 4Q 5Q 6Q 7Q 8Q C OC 2 5 6 9 12 15 16 19 10 9 8 7 6 5 4 3 25 24 21 23 2 1 74HC373 20 U4 1 2 3 17 16 U5A 29 5V 1 3 2 6 4 5 Y0 Y1 Y2 Y3 Y4 G1 Y5 G2AY6 G2BY7 A B C 22 15 14 13 12 11 10 9 7 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 VPP 33 pF C1 EA/VP 27 U3 U2 U1 PGM 31 1 5V O0 O1 O2 O3 O4 O5 O6 O7 11 12 13 15 16 17 18 19 CE OE 27C64 10 9 8 7 6 5 4 3 25 24 21 23 2 20 26 27 22 U6 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 D0 D1 D2 D3 D4 D5 D6 D7 11 12 13 15 16 17 18 19 CS1 CS2 WE OE MCM6264 74HC138 74HC08 Figura 60 – Sistema microprocessado baseado no 80C31. 96 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados O fato do 80C31 possuir um pino que é ativado apenas quando o microcontrolador quer buscar instruções tem uma implicação interessante. Como o pino PSEN, pode-se criar dois espaços de endereçamento: uma para memória de dados e dispositivos de E/S e outro para memória de programa. Na Figura 61 foi utilizado um outro 74HC138 para permitir que espaços de endereçamentos distintos. Com isso, o microcontrolador pode endereçar até 64 kbytes de memória de programa e até 64 kbytes de memória de dados. 19 12 MHz Y2 18 33 pF C2 5V C3 10 uF SW1 RST 100 k R1 14 15 12 13 9 1 2 3 4 5 6 7 8 EA/VP X1 X2 T0 T1 INT0 INT1 RESET P1.0 P1.1 P1.2 P1.3 P1.4 P1.5 P1.6 P1.7 P0.0 P0.1 P0.2 P0.3 P0.4 P0.5 P0.6 P0.7 ALE/P P2.0 P2.1 P2.2 P2.3 P2.4 P2.5 P2.6 P2.7 RD WR PSEN 39 38 37 36 35 34 33 32 30 3 4 7 8 13 14 17 18 11 21 22 23 24 25 26 27 28 1D 2D 3D 4D 5D 6D 7D 8D 1Q 2Q 3Q 4Q 5Q 6Q 7Q 8Q C OC 2 5 6 9 12 15 16 19 10 9 8 7 6 5 4 3 25 24 21 23 2 1 74HC373 20 U4 1 2 3 17 16 5V 29 80C31 6 4 5 A B C Y0 Y1 Y2 Y3 Y4 G1 Y5 G2AY6 G2BY7 22 15 14 13 12 11 10 9 7 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 VPP 33 pF C1 27 U2 U1 PGM 31 1 5V U3 O0 O1 O2 O3 O4 O5 O6 O7 11 12 13 15 16 17 18 19 CE OE 27C64 10 9 8 7 6 5 4 3 25 24 21 23 2 20 26 27 22 U6 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 D0 D1 D2 D3 D4 D5 D6 D7 11 12 13 15 16 17 18 19 CS1 CS2 WE OE MCM6264 74HC138 U4 1 2 3 U5A 1 5V 3 2 74HC08 6 4 5 A B C Y0 Y1 Y2 Y3 Y4 G1 Y5 G2AY6 G2BY7 15 14 13 12 11 10 9 7 74HC138 Figura 61 - Sistema microprocessado baseado no 80C31 com espaços de endereçamentos diferentes para dados e instruções. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 97 Curso de Microprocessadores Apêndice A - BASIC Stamp II Comentário Este apêndice contém um breve manual do BASIC Stamp II. O texto está em inglês e sem formatação, pois foi extraído de um arquivo texto (MANUAL.TXT) que acompanha o BS2. Manual do BASIC Stamp II BS2-IC Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 98 TX RX ATN GND P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 P10 P11 P12 P13 P14 P15 +5V * RES GND Name Serial output Serial input Serial attention Serial ground I/O pin 0 I/O pin 1 I/O pin 2 I/O pin 3 I/O pin 4 I/O pin 5 I/O pin 6 I/O pin 7 I/O pin 8 I/O pin 9 I/O pin 10 I/O pin 11 I/O pin 12 I/O pin 13 I/O pin 14 I/O pin 15 +5V supply Reset I/O pin System ground Description Connect to pin 2 of PC serial DB9 (RX) Connect to pin 3 of PC serial DB9 (TX) Connect to pin 4 of PC serial DB9 (DTR) Connect to pin 5 of PC serial DB9 (GND) Each I/O can source 20ma and sink 25ma. P0-P7 and P8-P15, as groups, can each source a total of 40ma and sink 50ma. 5-volt input or regulated output. Pull low to reset; goes low in reset Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados 24 PWR * Regulator input Voltage regulator input; takes 5-15 VDC Internal Perspective: The BS2 has 2K bytes of EEPROM which holds the executable BASIC program and any data. Memory not used by the BASIC program can be read and written at run-time as a data bank, or initialized with data at download time. This memory is only affected by downloading or run-time modification. There are 32 bytes of RAM which serve as variable space and I/O pin interface for the BASIC program. This memory can be accessed as words, bytes, nibbles, or bits. Each time the BASIC program is run anew, this memory is cleared to all zeroes. So, the 2K byte EEPROM is for program and data, and only affected by initial downloading or run-time modification. It survives power-down. The 32 bytes of RAM are for run-time variables and I/O pin access. This memory is cleared each time the BS2 is powered up, reset, or downloaded to. The 2K-byte EEPROM is arranged as follows: byte $000 ---+--- Data start | | | | | | | | | Data end | | | | | | | Program end | | | | | | | | byte $7FF ---+--- Program start The 32-byte RAM is arranged as follows: WORD $0- x x x x $1- x x x x $2- x x x x $3- x x x x $4- x x x x BITS xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx Description Pin input states Pin output latches Pin directions variable space variable space R/W read-only read/write read/write read/write read/write Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 99 Curso de Microprocessadores $5$6$7$8$9$A$B$C$D$E$F- xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx variable space variable space variable space variable space variable space variable space variable space variable space variable space variable space variable space read/write read/write read/write read/write read/write read/write read/write read/write read/write read/write read/write Word $0 always reflects the read-state of all 16 I/O pins. Whether a pin is an input or output, it's logical state can be read in this word. Word $0 is accessed by the following symbolic names: INS INL INH INA INB INC IND IN0 | | | IN15 the entire 16-bit word the low byte of INS the high byte of INS the low nibble of INL the high nibble of INL the low nibble of INH the high nibble of INH the low bit of INS - corresponds to pin P0 the high bit of INS - corresponds to pin P15 Word $1 contains the output latches for all 16 I/O pins. If a pin is in input mode, this data is unused, but when a pin is in output mode, its corresponding word $1 bit sets its state. The bits are all readable and writable, regardless of pin direction. The following symbolic names access word $1: OUTS the entire 16-bit word OUTL the low byte of OUTS OUTH the high byte of OUTS OUTA the low nibble of OUTL OUTB the high nibble of OUTL OUTC the low nibble of OUTH OUTD the high nibble of OUTH OUT0 the low bit of OUTS - corresponds to pin P0 | | | OUT15 the high bit of OUTS - corresponds to pin p15 Word $2 contains the direction bits for all 16 I/O pins. To place a pin in input mode, its corresponding word $2 bit must be cleared to 0. To put a pin into output mode, its corresponding word $2 bit must be set to 1, at which time its word $1 bit will determine whether it is high or low. Word $2 has 100 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados these symbolic names: DIRS DIRL DIRH DIRA DIRB DIRC DIRD DIR0 | | | DIR15 the entire 16-bit word the low byte of DIRS the high byte of DIRS the low nibble of DIRL the high nibble of DIRL the low nibble of DIRH the high nibble of DIRH the low bit of DIRS - corresponds to pin P0 the high bit of DIRS - corresponds to pin p15 Words $3-$F are for general purpose variable use and have no pre-assigned symbolic names. The VAR statement is used to allocate this memory. The above text has introduced the physical pin-out of the BS2 as well as the internal EEPROM, RAM, and I/O structure. Programming the BASIC Stamp II In the BASIC Stamp II, there are two general categories of BASIC statements: compile-time and run-time. Compile-time statements are resolved when you compile the program (Alt-R or Alt-M) and do not generate any executable code. Run-time statements, on the other hand, generate code and are executed at runtime. There are three compile-time statements. They are used for declaring variables, constants, and data. They are: VAR CON and DATA The VAR statement - for defining variables Your program should begin with a declaration of all of its variables. VAR statements assign symbolic names to variable RAM (RAM not used by I/O - words $3-$F). This is done as follows: 'Declare the variables cat mouse dog rhino snake var var var var var nib bit byte word bit(10) 'make "cat" a nibble variable 'make "mouse" a bit variable 'make "dog" a byte variable 'make "rhino" a word variable 'make "snake" a 10-piece bit variable The compiler will group all words, bytes, nibs, and bits, and respectively Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 101 Curso de Microprocessadores arrange them into unused RAM. By pressing Alt-M, you can see a picture of the RAM allocation. First, the three I/O words are shown, then all words, bytes, nibs, and finally, bits, are seen. Empty RAM follows. Alt-M is a quick way to assess how much RAM you've used. The VAR usage options are as follows: 'define unique variables sym1 sym2 sym3 sym4 VAR VAR VAR VAR bit nib byte word 'make a bit variable 'make a nibble variable 'make a byte variable 'make a word variable 'After bit/nib/byte/word a value may be placed 'within parentheses to declare an array size: sym5 VAR nib (10) 'make a 10 nibble array 'define variables-within-variables or alias variables sym6 sym7 sym8 VAR VAR VAR sym4.highbit sym4.lowbit sym2 'make a bit variable of sym4's highbit 'make a bit variable of sym4's lowbit 'make an alternate name for sym2 'When using VAR to assign non-unique variables (a variable 'name is used in lieu of bit/nib/byte/word(size)), a period may 'be placed after the variable name and followed by modifiers. 'Modifiers are used to identify sub-pieces of the initially'mentioned variable. sym9 VAR sym4.highbyte.lownib.bit2 'picky, picky... Here are all the variable modifiers: 102 LOWBYTE HIGHBYTE BYTE0 BYTE1 'low byte of a word 'high byte of a word 'byte0 (low byte) of a word 'byte1 (high byte) of a word LOWNIB HIGHNIB NIB0 NIB1 NIB2 NIB3 LOWBIT HIGHBIT BIT0 BIT1 'low nibble of a word or byte 'high nibble of a word or byte 'nib0 of a word or byte 'nib1 of a word or byte 'nib2 of a word 'nib3 of a word 'low bit of a word, byte, or nibble 'high bit of a word, byte, or nibble 'bit0 of a word, byte, or nibble 'bit1 of a word, byte, or nibble Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados BIT2 BIT3 BIT4 BIT5 BIT6 BIT7 BIT8 BIT9 BIT10 BIT11 BIT12 BIT13 BIT14 BIT15 'bit2 of a word, byte, or nibble 'bit3 of a word, byte, or nibble 'bit4 of a word or byte 'bit5 of a word or byte 'bit6 of a word or byte 'bit7 of a word or byte 'bit8 of a word 'bit9 of a word 'bit10 of a word 'bit11 of a word 'bit12 of a word 'bit13 of a word 'bit14 of a word 'bit15 of a word In summary, to declare variables, VAR statements are used. VAR statements either declare unique variables or variables-within-variables/alias-variables. For defining unique variables: symbol VAR size (array) - symbol is a unique name for a variable - size is either WORD, BYTE, NIB, or BIT - (array) is an optional expression which declares an array size For defining variables-within-variables or alias-variables symbol VAR variable.modifiers - symbol is a unique name for a variable - variable is a defined variable name - .modifiers are optional and used to define variables-within-variables The compiler will group all declarations by size (in the case of unique variables) and assign them to unused RAM. Alt-M lets you see the result of this process. Non-unique variables are in-whole or in-part derived from unique variables and get assigned within the unique-variable memory. Keep in mind that you may make alias names for the pin variables: keyin var in5 'make "keyin" a way to read P5's state. The CON statment - for defining constants The CON statement is similar to the VAR statement, except that it is for defining constant values and assigning them to symbolic names. This is handy for having a single declaration which gets accessed throughout your program. The CON syntax is as follows: Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 103 Curso de Microprocessadores symbol CON expression 'assign expression to "symbol" - symbol is a unique symbolic name for a constant - expression is a compile-time-resolvable constant level limit CON CON 10 10*4<<2 '"level" is same as 10 in program '"limit" is 160 expressions after CON can contain the following binary operators and are resolved left-to-right: + * / << >> & | ^ add subtract multiply divide shift left shift right logical AND logical OR logical XOR example: growth CON 100-light/gel '"light" and "gel" are CON's, too The DATA statment - for defining data EEPROM memory not used by your BASIC program can be used for data storage. Keep in mind that your BASIC program builds from the end of memory towards the start of memory. This allocation is automatic. Your data, on the other hand, builds from the start of memory towards the end. The sum of program and data memory cannot exceed the 2K byte limit. The compiler will always tell you when you have a conflict. DATA statements are used to build data into unused memory. Initially, the DATA location is set to 0. It is advanced by 1 for each byte declared. Here is an example DATA statement: table DATA "Here is a string..." Usually, you will want to precede DATA statements with a unique symbol name. The symbol name will be assigned a constant value (as if via CON) which is the current data pointer. The text following 'DATA' is usually a list of bytes which can be constant expressions. In the above example (assuming this was the first DATA statement in the program), "table" becomes a constant symbol of value 0; "Here is a string..." is broken into individual bytes and placed into EEPROM memory sequentially. Alt-M and two <SPACE>s will show you the result of this line. The DATA pointer may be altered at any time by an @ sign followed by a new 104 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados pointer value: list DATA @$100,"some data" DATA has a few variations of use to allocate defined and undefined data. Defined data is fully declared and known at compile time. Undefined data is the mere allocation of data space, while not assigning values into the bytes of EEPROM (to be done at run-time, instead). Defined and undefined data are declared as follows: for defined data: fee fie foe DATA 0,1,2,3,4,5,6,7,8,9 DATA word 1000 DATA 0 (256) 'actual bytes 'make two bytes: $E8 and $03 '256 bytes initialized as 0 for undefined data: fum DATA (1024) scratch DATA word (16) Important concept: 'reserved 1K byte of undefined data 'reserve 16 words of undefined data Defined DATA and BASIC program memory are always downloaded to the BS2. Undefined data and unused EEPROM memory are not downloaded. This allows you to change programs while keeping data, assuming both programs defined the same stretch of memory as undefined DATA. Alt-M will show you maps of EEPROM allocation. This download/don't-download rule is applied to 16-byte blocks. If any byte within a 16byte block is defined DATA or BASIC program, that whole block is downloaded. Use Alt-M to see this. In summary, DATA is used to define EEPROM byte usage that doesn't conflict with the BASIC program storage: - DATA can be preceeded by a symbol which will be assigned the constant value of the current DATA pointer. - Byte-size data is assumed, but 'word' can be used to break a word into two bytes of storage. - The @ sign is used to redirect the DATA pointer. If a symbol preceeds a DATA statement and the first thing after DATA is @, the new pointer value is assigned to the symbol. - Defined data is spelled out, so to speak, with numbers and letters. - Defined data may be repeated at the byte or word level using (array). Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 105 Curso de Microprocessadores - Undefined data may be reserved by using (array) unpreeceded by a value. Note: DATA can contain references to DATA symbols: t1 t2 t3 t4 DATA DATA DATA DATA "Here's "Here's "Here's "Here's table 1...",0 table 2...",0 table 3...",0 table 4...",0 t_starts DATA word t1, word t2, word t3, word t4 Run-Time Expressions ---------------------------------------------------Run-time expressions can contain constants, variables, operators, and parentheses. They are resolved using 16-bit math. Constants can be in several forms: label $BA1F %111001111 99 "A" - a label may be assigned a constant via CON - Hex - Binary - Decimal - ASCII Note: When more than one character is within quotes, they are separated by the compiler as such: "DOG" becomes "D","O","G". "String"+$80 becomes: "S","t","r","i","n","g"+$80. Variables can be accessed a number of ways: somevar wordvar.highbit nibarray(index) word.bit0(bitoffset) - Some variable - Use modifiers to access sub-variables - A variable followed by an expression in quotes is indexed as an array (0=1st element) - Scan a word, one bit at a time Say a variable were defined as such: string var byte (10) It could be accessed as: string - the 1st byte string (0) - the 1st byte string (1) - the 2nd byte string (9) - the 10th (last) byte string.lownib(nibindex) - nibindex could be 0-19 string.lowbit(bitindex) - bitindex could be 0-79 106 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados There are binary, unary, and conditional expression operators. Unary operators preceed a variable or constant or (expression) and have highest priority. They are as follows: SQR ABS ~ DCD NCD COS SIN - Square root of unsigned 16-bit value - Absolute of signed 16-bit value - One's complement of 16-bit value (bitwise not) - Two's complement of 16-bit value (negation) - 2^n decoder of 4-bit value (0...15 -> 1,2,4,8,16,...32768) - Priority encoder of 16-bit value (=>32768,=>16384,=>8192,...=1 -> 15,14,13,...1 ; 0 -> $FFFF) - Cosine of 8-bit value. Result is in the range of +-127, unit circle is 0-255 radial units. - Sine of 8-bit value. Result is in the range of +-127, unit circle is 0-255 radial units. Examples: sin bytevar sqr 50000 ~ in0 Binary operators take two terms and go between variables, constants, or expressions. They are as follows: & | ^ MIN MAX + * ** */ / // DIG << >> REV - Bitwise AND - Bitwise OR - Bitwise XOR - limit value to minimum - limit value to maximum - addition - subtraction - multiply - multiply and return high 16-bits of result - multiply and return middle 16-bits of result (use to simultaneously multiply by a whole and a part; ie. 'value */ $0180' multiplies by 1 and a half) - divide - divide and return remainder - return decimal digit; '12345 dig 3' returns 2 - shift left - shift right - reverse order of bits, lsb-justified; '%100110 rev 6' yields %011001 Examples: ypos * xsize + xpos randword // 20 countacc min 200 - 200 / 200 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 107 Curso de Microprocessadores Parentheses can be placed to special-order the pattern of expression resolution. Though unary operators have highest priority and binary operators have secondary priority, and with those rules expressions are resolved left-toright, parentheses can override priority: X+1*Y-1 (X+1)*(Y-1) 'something's wrong here if we need (X+1)*(Y-1) 'do it right Up to 8 levels of parentheses can be used. For use within conditional expressions (IF), there is a special unary operator and several binary operators. These conditional operators have highest priority of all. NOT - highest priority unary AND, OR, XOR - highest priority binaries Note: These are arithmetically identical to expression operators: ~ & | ^ though they differ in application. Lower-priority conditional binary operators (still higher than expression ops): < <= = => > <> - less than - less than or equal to - equal to - equal to or greater than - greater than - not equal Note: These comparison operators return 0 for false and $FFFF for true. Combined with NOT, AND, OR, and XOR, complex tests can be done. To summarize, here are some examples: outs = ~ dcd nibarray(index) 'lookup a nibble, decode it, not it IF x<1 or not y>3 and (z=0 xor r=3) then loopback Run-Time Instructions ---------------------------------------------------Anywhere a value is requested, an expression may be placed =============================================================================== DEBUG outputdata =============================================================================== Show variables and messages for debugging purposes. usage: DEBUG "Here we are!" 'show message when executed 108 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados When executed, the data after DEBUG will be sent to the PC for display. DEBUG data can be displayed in several modes. Straight data can be relayed to the PC, or you can have values printed in decimal, hex, binary, or ascii. In the number-printing modes, the result of an expression can be printed or a complete relation can be shown between the expression and its result. For example: DEBUG dec? X 'show variable X in decimal will yield (if x=1): X=1 DEBUG dec X 'show variable X in decimal yields: 1 To print numbers, there are several words which can preceed expressions: ASC? 'show ascii value w/relation STR bytevar 'output string from byte array - until 0 STR bytevar\length 'output string from byte array - length bytes REP value\count 'output value count times DEC value 'print value in decimal DEC1-DEC5 value 'print value in decimal - 1-5 digits SDEC value 'print value in signed decimal SDEC1-SDEC5 value 'print value in signed decimal - 1-5 digits HEX value 'print value in hex HEX1-HEX4 value 'print value in hex - 1-4 digits SHEX value 'print value in signed hex SHEX1-SHEX4 value 'print value in signed hex - 1-4 digits IHEX value 'print value in hex w/'$' IHEX1-IHEX4 value 'print value in hex w/'$' - 1-4 digits ISHEX value 'print value in signed hex w/'$' ISHEX1-ISHEX4 value 'print value in signed hex w/'$' - 1-4 digits BIN value 'print value in binary BIN1-BIN4 value 'print value in binary - 1-4 digits SBIN value 'print value in signed binary SBIN1-SBIN16 value 'print value in signed binary - 1-4 digits IBIN value 'print value in binary w/'%' IBIN1-IBIN16 value 'print value in binary w/'%' - 1-4 digits ISBIN value 'print value in signed binary w/'%' ISBIN1-ISBIN16 value 'print value in signed binary w/'%' -1-4 digits To print the literal text expression along with the result, follow any of the words with a '?'. A lone '?' is the same as 'DEC?'. REP cannot be followed by a '?', but ASC needs one. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 109 Curso de Microprocessadores DEBUG statements can contain many strings and numbers, separated by commas. In addition, there are several special control characters which are interpreted by the DEBUG screen: name value effect -----------------------------------------------------CLS 0 clears the screen and homes the cursor HOME 1 homes the cursor BELL 7 beep the PC speaker BKSP 8 backspace - backs up the cursor TAB 9 advances to the next 8th column CR 13 carriage return - down to next line DEBUG cls,dec ina," " 'cls, print ina in decimal, spaces too Example program using DEBUG: count var byte loop: debug sdec? sin count 'show the signed-decimal sine of count count = count + 1 'increment count if count <> 0 then loop 'loop until count rolls over =============================================================================== FOR...NEXT =============================================================================== The FOR...NEXT loop. usage: FOR variable = start TO end {STEP stepval} {some code} NEXT STEP is for specifying a step value other than the default of 1; if specified, stepval must be positive since whether to add or subtract stepval from variable is determined dynamically at run-time (this allows '10 TO 0' without specifying a negative STEP value.). FOR...NEXT loops can be nested up to 16 deep. Note: NEXT is stand-alone and implies the variable from the last FOR statement. =============================================================================== BRANCH =============================================================================== Branch according to an index. usage: BRANCH index,[label0,label1,label2,...labelN] If index=0, a GOTO label0 will be executed. If index=1, a GOTO label1 will be 110 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados executed. If the index exceeds the number of label entries, no branch will occur and execution will proceed at the next instruction. =============================================================================== IF =============================================================================== Branch conditionally. usage: IF conditionalexpression THEN label ie. IF x=1 then redoit If the result of conditionalexpression is not 0, execution will be continued at label. Else, execution continues at the next instruction. =============================================================================== GOTO =============================================================================== Go to a new point in the program. usage: GOTO joe A branch to joe will occur, rather than execution continuing at the next instruction. =============================================================================== GOSUB =============================================================================== Go to a subroutine (and then RETURN later). usage: GOSUB ledset The execution point is stored and then a branch to ledset occurs. When a RETURN is encountered (in ledset), execution continues at the instruction following the GOSUB. GOSUB's may be nested up to 4 deep and you are allowed 255 of them in your program. =============================================================================== RETURN =============================================================================== Return from a subroutine. usage: RETURN The execution point is set to the instruction after the GOSUB that got into the subroutine executing the RETURN. If a RETURN is executed without a corresponding GOSUB, execution begins anew Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 111 Curso de Microprocessadores at the start of the program. =============================================================================== Variable Assignment =============================================================================== assign a value to a variable. usage: variable = value Value is an expression which gets written to variable. =============================================================================== LOOKUP =============================================================================== Lookup a variable according to an index. usage: LOOKUP index,[value0,value1,value2,...valueN],variable If index=0, value0 will be written to variable. If index=1, value1 will be written to variable. If the index exceeds the number of value entries, the variable will not be affected. =============================================================================== LOOKDOWN =============================================================================== Lookdown a value and return an index. usage: LOOKDOWN value,??[value0,value1,value2,...valueN],variable ?? is a comparison operator: =,<>,>,<,<=,=> (= is the default). A comparison is made between value and value0; if the result is true, 0 is written into variable. If that comparison was false, another comparison is made between value and value1; if the result is true, 1 is written into variable. This process continues until a true is yielded, at which time the index is written into variable, or until all entries are exhausted in which case variable is unaffected. =============================================================================== RANDOM =============================================================================== Pseudo-randomly iterate a word variable. usage: RANDOM wordvariable wordvariable will be iterated 16 times to potentially change every bit. =============================================================================== SEROUT tpin,baudmode,{pace,}[outputdata] SEROUT tpin\fpin,baudmode,{timeout,tlabel,}[outputdata] =============================================================================== 112 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados Output data serially. usage: SEROUT 3,6+$4000,["The temperature is ",dec temp," degrees.",cr] Tpin is 0-15 for an I/O pin, or 16 for the internal serial port. Fpin is an option to specify a flow-control pin. Baudmode is a composite value which specifies baud rate, parity mode, true/inverted output, and open/driven output. If no fpin was specified, an optional pace in milliseconds may be declared to pace characters by a number of milliseconds. If an fpin was specified, an optional timeout (in milliseconds) and timeout label may be specified to enable timeout and branching for flow-control. Baudmode is the bit period expressed in microseconds-20 (ie 300 baud is 3313). An easy formula for computing the baud rate (baudmode bits 0-12) is: int (1,000,000/baud) - 20 baudmode's bit 15 ($8000) is 0 for driven, 1 for open-drain/source (SEROUT) baudmode's bit 14 ($4000) is 0 for true, 1 for inverted. baudmode's bit 13 ($2000) is 0 for 8-bit no parity, 1 for 7-bit parity outputdata follows the DEBUG conventions note: If tpin=16 (internal port), baudmode's bits 14 and 15 are ignored. However, they will affect operation of fpin. =============================================================================== SERIN rpin{\fpin},baudmode,{plabel,}{timeout,tlabel,}[inputdata] =============================================================================== Input data serially. usage: SERIN 16,32+$2000,60000,nothingcame,[WAIT (","),STR bytearray\16\","] Rpin is 0-15 for an I/O pin, or 16 for the internal serial port. Fpin is an optional flow-control pin. Baudmode is described in SEROUT. Plabel is a an option to specify a where to branch in the event of a parity error (parity mode must be enabled). Timeout is an optional value to specify how long to wait in milliseconds before giving up and branching to tlabel. Inputdata follows the conventions below: variable 'input a byte and store into variable STR bytearray\L{\E} 'input a string into bytearray of 'length L with optional end-character 'of E (0's will fill remaining bytes) SKIP L 'Input and ignore L bytes WAITSTR bytearray 'Wait for bytearray string (bytearray Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 113 Curso de Microprocessadores 'is 0-terminated) WAITSTR bytearray\L 'Wait for bytearray string of length L WAIT (value,value,...) 'Wait for up to a six-byte sequence DEC variable 'input decimal value DEC1-DEC5 variable 'input decimal value of fixed length SDEC variable 'input signed decimal value SDEC1-SDEC5 variable '" of fixed length HEX variable 'input hex value HEX1-HEX4 variable '" of fixed length SHEX variable 'input signed hex value SHEX1-SHEX4 variable '" of fixed length IHEX variable 'input indicated ($) hex value IHEX1-IHEX4 variable '" of fixed length ISHEX variable 'input signed inidicated ($) hex value ISHEX1-ISHEX4 variable '" of fixed length BIN variable 'input binary value BIN1-BIN16 variable '" of fixed length SBIN variable 'input signed binary value SBIN1-SBIN16 variable '" of fixed length IBIN variable 'input indicated (%) binary value IBIN1-IBIN16 variable '" of fixed length ISBIN variable 'input signed indicated (%) bin value ISBIN1-ISBIN16 variable '" of fixed length note: If rpin=16 (internal port), baudmode's bits 14 and 15 are ignored. However, they will affect operation of fpin. =============================================================================== READ =============================================================================== Read a byte from the EEPROM. usage: READ location,variable Location is 0-2047. Variable will receive the byte read from location. =============================================================================== WRITE =============================================================================== Write a byte into the EEPROM. usage: WRITE location,byte Location is 0-2047. Byte is 0-255. Byte will be written into location. =============================================================================== XOUT 114 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados =============================================================================== Output X-10 codes to a TW523 or TW513 module (by X-10 Powerhouse). usage: XOUT mpin,zpin,[house\keyorcommand{\cycles,...] Mpin will be made a low output and is the modulation control. Zpin will be made an input and is the zero-crossing detect. House is the house code (0-15 is 'A'-'P'). Keyorcommand is a key (0-15 is '1'-'16') or command (see table below). Cycles is an optional number which overrides the default of two; this should only be used with 'dim' and 'bright' commands. command symbol UNITON UNITOFF UNITSOFF LIGHTSON DIM BRIGHT value %10010 %11010 %11100 %10100 %11110 %10110 TW513/TW523-to-BS2 connections: 1-to-zpin, 2-to-VSS, 3-to-VSS, 4-to-mpin (also zpin to VDD via 10K resistor) =============================================================================== SHIFTOUT =============================================================================== Shift bits out synchronously. usage: SHIFTOUT dpin,cpin,mode,[data{\bits},...] Dpin is the data output, cpin is the clock output. Mode is 0 for lsb-first or 1 for msb-first. Data is a value to be shifted out. Bits is an optional bit count (8 is the default). The following symbols are defined for use with SHIFTOUT: symbol value --------------------LSBFIRST 0 MSBFIRST 1 =============================================================================== SHIFTIN =============================================================================== Shift bits in synchronously. usage: SHIFTIN dpin,cpin,mode,[variable{\bits},...] Dpin is the data input, cpin is the clock output. Mode is 0 for msb-first/ pre-clock, 1 for lsb-first/pre-clock, 2 for msb-first/post-clock, or 3 for Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 115 Curso de Microprocessadores lsb-first/post-clock. Variable receives the shifted-in data. Bits is an optional bit count (8 is the default). The following symbols are defined for use with SHIFTIN: symbol value --------------------MSBPRE LSBPRE MSBPOST LSBPOST 0 1 2 3 =============================================================================== COUNT =============================================================================== Count cycles on a pin for some milliseconds. usage: COUNT pin,period,variable Pin will be placed in input mode. For 'period' milliseconds, cycles will be counted on pin. Both sides of the waveform must be at least 4us in duration, limiting the input frequency to 125KHz (assuming 50/50 duty cycle). The result (0-65535) will be written into variable. =============================================================================== PULSOUT =============================================================================== Output a timed pulse. usage: PULSOUT pin,period Pin will be made an output opposite of it's OUTx value for period*2us. Pin will be left in output mode with OUTx state. =============================================================================== PULSIN =============================================================================== Input a timed pulse. usage: PULSIN pin,state,variable Pin will be placed in input mode. A pulse of 'state' will be waited for and measured with 2us resolution and the result will be written into variable. If an overflow occurs while waiting for any edge, (>65535*2us or >131ms), 0 will be written into variable. =============================================================================== BUTTON =============================================================================== Debounce button, auto-repeat, and branch if button is in target state. 116 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados usage: BUTTON pin,downstate,delay,rate,bytevariable,targetstate,label Pin will be placed in input mode. Downstate is the state which is read when the button is pressed. Delay specifies down-time before auto-repeat in BUTTON cycles. Rate specifies the auto-repeat rate in BUTTON cycles. Bytevariable is the workspace. It must be cleared to 0 before being used by BUTTON for the first time. Targetstate specifies what state (0=not pressed, 1=pressed) the button should be in for a branch to occur. Label specifies where to go if the button is in the target state. =============================================================================== INPUT, OUTPUT, LOW, HIGH, TOGGLE, REVERSE =============================================================================== Modify a pin's input/output high/low state. usage: INPUT pin Pin is 0-15. modified: OUTx DIRx ---------------------------INPUT same 0 OUTPUT same LOW 0 1 HIGH 1 1 TOGGLE flip REVERSE same 1 1 flip =============================================================================== FREQOUT =============================================================================== Output a sine-wave(s) for some time. usage: FREQOUT pin,milliseconds,freq1{,freq2} Pin will be temporarily placed in output mode for modulation. Milliseconds specifies how long to output the frequency(s). Freq1 specifies the (first) frequency in 1Hz units (0-32768Hz). Freq2 is optional and specifies a second frequency. The pin may be filtered by an RC circuit to achieve a clean sine-wave(s). High-Z speakers may be driven with a coupling cap and a filter cap. =============================================================================== DTMFOUT =============================================================================== Output DTMF tones. Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 117 Curso de Microprocessadores usage: DTMFOUT pin,{ontime,offtime,}[key,key,key,...] Pin will be temporarily placed in output mode for modulation. The default on and off times are 200ms and 50ms. ontime and offtime are optional overrides which specify milliseconds. Key(s) are 0-15; 0-9 are the digits '0'-'9'; 10 is '*'; 11 is '#'; 12-15 are 'A'-'D' which are fourth-column codes unavailable on normal phones. The pin may be filtered by an RC circuit to achieve a clean sine-waves. High-Z speakers may be driven with a coupling cap and a filter cap. =============================================================================== PWM =============================================================================== Pulse-width modulate a pin for some time. usage: PWM pin,duty,cycles Pin will be temporarily made an output while it is modulated. Duty is an 8-bit value (0-255) which specifies the duty-cycle. Cycles is the number of 256 pin update periods which take ~1ms. When done, pin will be placed in input mode to allow the voltage to remain until another PWM is executed. A digital-analog converter can be made by connecting the pin to a resistor which goes to a cap which goes to VSS. The resistor-cap junction will reflect the duty (0-5V) during and after PWM. =============================================================================== RCTIME =============================================================================== Measure an R-C charge/discharge time. usage: RCTIME pin,state,variable Pin will be placed in input mode and time will be measure in 2us units while pin is in 'state'. The result will be written into variable. If an overflow occurs (>131ms), 0 will be written. This is useful for measuring resistor-capacitor charge/discharge times. Since the logic threshold of a pin is ~1.4 volts, it is best to have the RC voltage go from the 5V supply towards ground, yielding a span of ~3.6 volts. For example, to convert a potentiometer setting to a number: connect a .1uf cap to VDD (5V); the other side of the cap goes to both the pin and the center-tap of the pot; the side-tap of the pot goes to VSS (ground); make the pin high for 1ms, then do RCTIME; the resistor-cap junction will start at 5V and fall towards ground. When ~1.4 is reached, the counting will terminate and the result will be returned to variable. Adjusting the pot causes the returned value to go up or down. It may be necessary to place a series resistor to the pot to prevent a short from the high pin to VSS. 118 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ Capítulo 5- Projeto de Sistemas Microcontrolados =============================================================================== NAP =============================================================================== Nap for a short period. usage: NAP x Enter low-power mode for a short period. When the period is over, the I/O's will go high-z for ~18ms and execution will continue at the next instruction. The x values for NAP are as follows: x ~seconds ---------------0 .018 1 .036 2 .072 3 .14 4 .29 5 .58 6 1.2 7 2.3 =============================================================================== SLEEP =============================================================================== Sleep for x seconds (x=0 to 65535). usage: SLEEP x Enter low-power mode and keep I/O's updated. Every ~2.3 seconds the I/O's will go high-z for ~18ms. Approximately 50ua average current will be consumed. When x seconds have been accrued in SLEEP mode, execution continues at the next instruction. Though the granularity of SLEEP is ~2.3 seconds, the error is within 1% over extended periods of time. =============================================================================== END =============================================================================== End program. usage: END Enter low-power mode and keep I/O's updated. Every ~2.3 seconds the I/O's will go high-z for ~18ms. Approximately 50ua average current will be consumed. END is terminated only by a hardware reset. =============================================================================== PAUSE Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/ 119 Curso de Microprocessadores =============================================================================== Pause for x milliseconds (x=0 to 65535). usage: PAUSE x A delay of x milliseconds will occur. =============================================================================== STOP =============================================================================== Stop execution. usage: STOP Execution is frozen, but low-power mode is not entered. This is like END, except that the I/O's never go high-z; they remain driven. Reset will end STOP. 120 Universidade Estadual de Londrina – Professor José Alexandre de França - http://www.josealexandre.rg3.net/