capítulo 3 processadores de propósito geral: softwareattux/cap3_09_05.pdf · processadores de...
TRANSCRIPT
Capítulo 3 – Processadores
de Propósito Geral: Software
Prof. Romis Attux
EA075 – 2015
Obs: Os slides são parcialmente baseados nos dos autores do livro texto e do Prof. Levy Boccato
Processadores de Propósito
Geral Um processador de propósito geral
(PPG) é um sistema “universal”, ou seja, um sistema computacional capaz de resolver uma ampla gama de tarefas.
Há vantagens e desvantagens de se usar um PPG. Falemos das vantagens.
O custo por unidade pode ser baixo, o desempenho pode ser muito bom, e tamanho e consumo podem até mesmo ser razoáveis.
Processadores de Propósito
Geral O custo NRE do projetista do sistema
embarcado tende a ser pequeno, pois o foco do projeto estará no software. Isso também tende a gerar um baixo tempo de prototipagem e um baixo TTM, além de gerar alta flexibilidade devida à operação no nível do software.
Arquitetura Básica
Três elementos centrais: Datapath, unidade de controle e memória.
Datapath (Caminho de Dados)
O caminho de dados realiza tarefas de processamento e dispõe de funcionalidades para armazenamento temporário da informação (registradores).
O coração do processamento é a ULA (unidade lógica e aritmética), que realiza várias operações matemáticas e lógicas e armazena diversos sinais de status pertinentes (zero, carry, overflow etc.).
Datapath (Caminho de Dados)
Dados temporários armazenados em registradores
podem incluir:
◦ Dados trazidos da memória e não processados.
◦ Dados gerados pela ULA que ainda serão necessários
para processamento posterior.
◦ Dados que serão mandados de volta para a memória.
◦ Dados que podem ser movidos de uma posição de
memória para outra
Um barramento interno transporta os dados no
datapath enquanto outro barramento faz a
comunicação com a memória.
Datapath (Caminho de Dados)
É usual falar no “tamanho de um processador”, e a largura do barramento do caminho de dados é a métrica crucial aqui.
Um processador de N bits pode ter registradores de N bits, uma ULA operando com N bits etc. Tamanhos comuns (ou clássicos) são 8, 16, 32 e 64 bits.
Datapath (Caminho de dados)
Unidade de Controle (UC)
Se o caminho de dados é uma orquestra que executa uma partitura (programa armazenado), a unidade de controle é um maestro obcecado que dá todas as deixas para seus músicos.
Basicamente, a UC possui funcionalidades de busca e decodificação de instruções e gera sinais capazes de manipular o caminho de dados para realizar essas instruções.
Unidade de Controle (UC)
Falaremos de dois registradores que a UC possui:
◦ PC (Program Counter): registrador que contém o endereço
da próxima instrução a ser buscada na memória.
◦ IR (Instruction Register): registrador que guarda a
instrução buscada na memória.
A unidade também possui circuitaria sequencial para
gerar sinais de controle para o caminho de dados.
A frequência de relógio dá uma idéia da velocidade do
processador, pois ela influencia o ritmo da pulsação
básica fetch-decode-execute.
Unidade de Controle (UC)
A UC também determina o próximo valor do PC. Se as instruções não envolvem saltos (jumps ou branches), a operação corresponde a um incremento.
Para uma instrução de branch, é usual que se analisem “flags” gerados pela ULA para determinação do próximo endereço.
O tamanho do PC determina o tamanho do espaço de endereçamento do processador.
Se o PC contém M bits, ele pode apontar para 2M endereços distintos de memória.
Unidade de Controle (UC)
A unidade de controle, tipicamente, pulsa num ritmo de busca de instrução, decodificação, busca de operandos, execução pelo caminho de dados e armazenamento dos resultados.
Cada estágio pode demandar um ou mais ciclos de clock.
O caminho pelo datapath que resulta no maior tempo de execução é chamado de caminho crítico.
Unidade de Controle (UC)
Memória
A memória de um computador, via de regra, armazena informação utilizada a médio e longo prazos.
Essa informação pode ser parte de um programa ou de um conjunto de dados.
Quando dados e programa ocupam o mesmo espaço de memória, fala-se em arquitetura Princeton.
Quando há uma separação, fala-se em arquitetura Harvard.
Memória - Tipos
Tipos Básicos de Memória
Memória ROM (Read-Only Memory): memória que pode apenas ser lida (embora algumas variedades mais modernas, como EPROMs, possam sofrer escrita).
Memória RAM (Random-Acess Memory): memória que pode ser agilmente lida e escrita.
Um sistema embarcado, por vezes, usa uma memória ROM para gravar o programa (função única).
Memória
On-chip: no mesmo chip do processador.
Off-chip: externa ao processador. A primeira é acessada mais
rapidamente, mas, por outro lado, ocupa espaço junto ao CI.
Hierarquia de Memória e
Memória Cache Veremos mais detalhadamente o conceito de
hierarquia de memória, mas, por ora, podemos pensar numa mescla de diferentes tipos de memória (com capacidades e velocidades de acesso distintas) que se interconectam de modo a explorar localidades temporais e espaciais presentes nos códigos computacionais.
No caso, falaremos rapidamente de memória cache e de sua relação com a chamada memória principal do computador.
Memória Cache
A memória cache é uma memória de dimensão
reduzida (em relação) à memória principal, mas que
possui maior velocidade.
Normalmente se encontra no chip e é uma RAM
estática “rápida”, em contraste com a RAM dinâmica da
memória principal.
A idéia é que a localidade espaço-temporal presente
nos códigos permita aproveitar bastante a informação
contida na cache (cache hits) antes que seja preciso
buscar informação nova (cache miss) na memória
principal (mais lenta).
Memória Cache
Execução de Instruções
Pode-se pensar, de maneira geral e simples, na execução de instruções em termos de cinco passos fundamentais: ◦ Busca da Instrução (Fetch Instruction)
◦ Decodificação da Instrução
◦ Busca dos Operandos (Fetch Operands)
◦ Execução
◦ Armazenamento dos Resultados
Pipelines
A execução de instruções pode ser significantemente acelerada através do uso de pipelines na execução das instruções.
Basicamente, pipelines são “linhas de produção” de execução de instruções, que exploram o paralelismo intrínseco à execução de uma sequência de instruções.
Um exemplo simples nos ajudará a compreender isso.
Pipelines
Pipelines
Se pensarmos nos cinco estágios descritos, poderíamos ter, por exemplo, a busca por uma instrução sendo feita enquanto se decodifica uma instrução anterior.
O desempenho de pipelines cheios numa operação sequencial é excepcional, pois cada ciclo significa a execução de uma instrução.
Porém, a existência de branches significa uma dificuldade, pois esse tipo de instrução pode gerar desvios no fluxo de execução.
Execução com Pipeline (Caso
Ideal)
Pipelines
Uma possibilidade é simplesmente parar o pipeline quando há um branch até que o endereço de execução se resolva. Isso causa bolhas no pipeline.
Outra possibilidade é usar algoritmos de previsão de branches para tentar evitar, ao menos em certos casos, bolhas. Um erro de predição, naturalmente, levaria à penalidade de remontar todo o fluxo do pipeline.
Bolha num Pipeline Causada
por Erro no Tratamento de um
Branch (6 Estágios)
Outros Problemas • Quando diferentes estágios precisariam do
mesmo recurso ao mesmo tempo, podem também surgir bolhas.
• No exemplo a seguir, o pipeline busca um operando na memória no mesmo instante em que deveria buscar uma instrução na memória causando um atraso de um ciclo de operação.
Bolha Estrutural
Outros Problemas
Podem surgir bolhas também pela necessidade de acessar um mesmo dado por instruções distintas.
Considere o caso a seguir: ◦ A A + B
◦ C C - A
A segunda instrução precisará do valor de A, mas ela não o terá até que a primeira se conclua.
O que ocorre está na figura a seguir.
Bolha – Dados em Comum
Visão do Programador • O programador escreve as instruções que
fazem com que o processador de propósito geral realize as tarefas desejadas. Ele pode ter uma visão mais ou menos detalhada da arquitetura subjacente, mas um mínimo é sempre necessário.
• A programação em alto nível, feita usualmente por meio de linguagens estruturadas, leva a programas com instruções genéricas e muito próximas de um algoritmo.
Visão do Programador
Por outro lado, quando se programa em Assembly, utiliza-se diretamente o conjunto de instruções do processador. Com isso, o programador obrigatoriamente tem de ter uma visão clara da arquitetura.
A tradução de linguagens de alto nível para as linguagens assembly é feita por programas conhecidos como compiladores, que são essenciais para que a máquina possa efetivamente “dar vida” ao código.
Visão do Programador
Ainda existe um nível mais baixo, o do código de máquina, no qual as instruções são codificadas diretamente como zeros e uns. O uso de assemblers serve exatamente para evitar essa necessidade quando se deseja programar em baixo nível.
Conjunto de Instruções
Quando programa em assembly, é preciso conhecer o conjunto de instruções da máquina (lembrem-se das experiências com o ARM!).
Uma instrução tem tipicamente duas
partes – um código de operação (opcode) e campos relativos a operandos.
Tipos de Instruções
Transferência de dados: servem para mover dados entre a memória e registradores, entre canais de entrada saída e registradores e entre registradores.
Instruções lógico-aritméticas: configuram a ULA para realizar determinadas funções, ou mover resultados para determinados registradores.
Branches podem ser pulos incondicionais, pulos condicionais ou chamadas / retornos de procedimentos.
Operandos
Um campo de operando pode indicar dados que farão parte da operação (fonte) ou um local para armazenagem de resultados (destino).
O número de operandos especificados por instrução varia de processador para processador e pode variar entre as próprias instruções.
Um campo de operando por usar vários modos de endereçamento.
Modos de Endereçamento
Conjunto de Instruções Simples
Programas – C e Assembly
Vejamos um programa numa linguagem de alto nível e seu equivalente assembly.
Cuidados
Avaliar o espaço disponível para dados e programa.
Conhecer os registradores da arquitetura e suas funções específicas quando for o caso.
Conhecer a forma de lidar com entrada e saída provida pelo processador.
Interrupções
Quando ocorre uma interrupção, o processador para o fluxo normal de execução e pula para uma rotina de serviço (que é projetada para tratar aquilo que causou a interrupção).
O processador precisa salvar o PC e colocar nele o endereço da rotina de serviço. Ele também precisa salvar outros elementos do contexto (e.g. o conteúdo de certos registradores).
Interrupções
Quando volta da rotina de serviço, o processador restaura o
conteúdo original do PC e, eventualmente, de outros
elementos, e segue o fluxo de execução.
Cada rotina precisa estar numa região distinta da memória, e
o programador deve estar atento a isso.
Um exemplo seria o tratamento de um evento associado a
um periférico (e.g. um botão). Pode-se utilizar uma posição
de memória para registrar esse evento e verificá-la
periodicamente, mas seria mais interessante escrever uma
rotina de serviço e associá-la ao pino conectado ao botão.
Apertado o botão, gera-se a interrupção e chama-se a rotina.
Sistema Operacional
Um sistema operacional é uma camada de software que provê serviços de baixo nível para a camada de aplicação, um conjunto de um ou mais programas executando na CPU consumindo e produzindo dados.
As tarefas do sistema envolvem carregar e executar os programas, compartilhar e alocar recursos do sistema para esses programas e proteger esses recursos contra corrupção.
Sistema Operacional
Um dos recursos mais importantes é a CPU que é
compartilhada por vários programas em execução. O
sistema decide qual programa é executado na CPU e
por quanto tempo, e usa o conceito de processo para
isso.
O gerenciamento de processos é uma tarefa crucial
que será estudada em detalhes em outros cursos.
Outro recurso importante gerenciado pelo sistema é a
memória, incluindo aí o conteúdo armazenado em
disco. Falaremos disso mais adiante.
Sistema Operacional
Além disso, o sistema provê software necessário para lidar com várias
interrupções de hardware, e drivers para comandar periféricos.
Um conceito importante é o de chamada de sistema (system call), um mecanismo
pelo qual uma aplicação invoca o sistema (o que lembra a chamada de uma
função numa linguagem de alto nível).
Um programa pode gerar uma chamada desse tipo quando precisa de algum
serviço provido pelo sistema.
Em suma, o sistema operacional “esconde” do usuário uma série de aspectos de
baixo nível, favorecendo um uso “simples”. As chamadas de sistema fazem uma
ponte entre a camada de aplicação e as camadas de mais baixo nível.
Ambiente de Desenvolvimento
Ambiente de Desenvolvimento
Há similaridades, mas também diferenças importantes
entre a forma de criar um programa em um desktop e
num sistema embarcado. Num desktop, poderíamos ter
um esquema como a seguir.
Ambiente de Desenvolvimento
Num sistema embarcado, tipicamente o processador usado é
diferente daquele em que ocorreu o desenvolvimento. Deve haver
compatibilidade.
Há várias ferramentas importantes:
◦ Compiladores: traduzem programas de alto nível em linguagem assembly (ou
mesmo de máquina). Compiladores cruzados são muito importantes, pois
permitem que uma máquina gere código para outra.
◦ Assemblers (Montadores): traduzem instruções assembly em instruções de
máquina.
◦ Linkers: permite que o programador crie programas em diferentes arquivos
compilados ou montados.
Teste de Software
Uma parte importante do desenvolvimento de software é a fase de teste, na qual o código desenvolvido deve ser avaliado em diversas condições e com diversas entradas.
É uma fase difícil mas crucial, pois um erro sério pode inviabilizar a operação do sistema embarcado.
Teste de Software
Para o caso de sistemas embarcados, há desafios adicionais: ◦ Se a aplicação for de tempo real, é
preciso levar em conta o fator tempo no teste.
◦ O sistema embarcado interage com o ambiente, o que traz novas variáveis que devem ser levadas em conta no teste.
Debuggers
Debuggers são ferramentas que auxiliam o projetista a avaliar e corrigir seus programas.
Eles permitem a execução passo-a-passo, a inserção de breakpoints etc.
Como rodam na máquina de desenvolvimento, eles podemos ser chamados de simuladores de conjunto de instruções ou máquinas virtuais.
Emuladores
Emuladores basicamente dão suporte ao processo de debugging enquanto o programa roda no processador alvo.
Basicamente, os emuladores são dispositivos específicos conectados ao processador alvo, através de uma placa, por meio de um cabo. A placa contém o processador alvo e circuitaria de suporte. Pode ainda haver outro cabo ligado a um dispositivo com a mesma configuração de pinos do processador alvo, o que permite a colocação em um sistema embarcado real (o que pode ser caro).
Device Programmers
Os device programmers carregam, a partir da máquina de desenvolvimento, um programa em linguagem de máquina desenvolvido pelo processador de trabalho no processador alvo para que ele possa ser testado em campo, por exemplo.
Modalidades de Teste
Se usarmos um processo de debugging com simulador,
teremos velocidade e facilidade, mas é limitado em
termos de interação com o ambiente.
Se usarmos um emulador, teremos um ciclo mais
longo, pois teremos de carregar código no emulador,
mas ele pode interagir com o restante do sistema.
Por fim, o ciclo usando o programador requer que o
processador seja retirado do sistema, programado, e
recolocado, o que gera uma demora expressiva, mas é
um caso realista (embora com pouco controle de
debug).
ASIPs
Falaremos agora brevemente de ASIPs, que são
programáveis como processadores de propósito geral,
mas possuem certas funcionalidades próprias.
Microcontroladores são ASIPs que incluem,
tipicamente:
◦ Dispositivos de conversão analógico-digital, timers e
dispositivos de comunicação serial no chip.
◦ Podem prover acesso direto por parte do programador a
certos pinos.
◦ Provêem algumas operações típicas de controle
embarcado, como operações de manipulação de bit.
ASIPs
Digital Signal Processors (DSPs) são altamente otimizados para operações usadas no tratamento de sinais de informação.
Isso pode significar um poder expressivo de computação numérica (registradores e ULAs, por exemplo), e a incorporação de funções típicas de filtragem e análise espectral.
Também são incorporadas funcionalidades de conversão analógico-digital, timers, contadores etc.
Projeto de um Processador de
Propósito Geral De certa forma, um processador de
propósito geral lembra um processador específico que processa instruções, com um caminho de dados de grande generalidade, armazenadas na memória.
Faremos um projeto didático a seguir que ilustra isso. Recordemos, para tanto, a arquitetura de um processador de propósito geral.
Projeto de um Processador de
Propósito Geral
Projeto da Unidade de Controle
Comecemos criando a unidade de controle. Ela utilizará o PC (16 bits), o IR (16 bits), uma memória de 64k x 16 bits, e um arquivo de registradores 16 x 16 bits.
O estado inicial, Reset, faz com que o PC seja zero.
O estado Fetch lê M[PC] em IR. O estado Decode gera um ciclo para que o IR seja atualizado e se tenha acesso às informações necessárias.
Projeto da Unidade de Controle
De posse da informação do IR, fazem-se múltiplos arcos, que representam as diferentes instruções expressas pelo repertório de opcodes.
Considerar-se-ão instruções Mov1 (memória registrador), Mov2 (registrador memória), Mov3 (registrador memória / indireto), Mov 4 (valor registrador), Add (soma entre o conteúdo de registradores), Sub (subtração entre o conteúdo de registradores), Jump (atualiza o PC com um valor de endereço usando a ULA).
Ilustração da Unidade
Caminho de Dados
A criação do caminho de dados vai pressupor que a ULA é capaz de realizar todas as operações pertinentes. Também pressuporá as funcionalidades necessárias para o arquivo de registradores.
Caminho de Dados
Caminho de Dados
(Conferir)
Diferenças Fundamentais
Um processador de propósito único “cristaliza” um programa em sua unidade de controle, enquanto um processador de propósito geral possui a flexibilidade de um programa armazenado.
O caminho de dados de um processador de propósito único é customizado para a aplicação, enquanto o caminho de dados de um processador de propósito geral deve ser flexível.