flowchart TB
subgraph CONC["Fundamentos conceituais"]
AO["Arquitetura × Organização"]
HIST["5 gerações<br/>RISC × CISC"]
VN["Von Neumann"]
HV["Harvard / Harvard mod."]
end
subgraph CASO["Estudo de caso e métricas"]
PIC["PIC18F4550<br/>(blocos, ciclo, pipeline)"]
MET["Métricas:<br/>T = N·CPI·Tcy<br/>IPC, MIPS, Amdahl, Gustafson"]
end
subgraph PRAT["Prática (Projeto Integrador)"]
T1["Tarefa 1<br/>Ambiente + LED"]
T2["Tarefa 2<br/>Quadro Arq/Org<br/>Harvard mod no .map"]
T3["Tarefa 3<br/>Medição de tempo<br/>no osciloscópio"]
end
CONC --> CASO --> PRAT
PRAT --> M2["Módulo 02:<br/>Representação de Dados"]
Módulo 1: Fundamentos de Arquitetura e Organização de Computadores
Esta é a versão enxuta do Módulo 1, pensada para você revisar antes da aula, durante deslocamentos ou na véspera de uma avaliação. Mantenho aqui os conceitos, diagramas e exemplos indispensáveis; o livro do módulo continua sendo a fonte completa para aprofundamento.
O Problema Que Abre o Semestre
Provocação inicial: dois processadores Intel executando exatamente o mesmo binário x86, sem alterar uma linha de código, podem diferir em cinco vezes no mesmo benchmark. Se ambos rodam o mesmo programa, como isso é possível? A resposta exige separar duas palavras que a literatura técnica costuma misturar: arquitetura e organização. Este módulo constrói essa separação e apresenta o microcontrolador que será seu companheiro de bancada nas próximas quinze semanas: o PIC18F4550 instalado no KIT ACEPIC PRO V8.2.
O caminho tem cinco etapas: distinção arquitetura/organização com critério prático; reconstrução histórica em cinco gerações e a controvérsia RISC versus CISC; os modelos Von Neumann e Harvard, com a variante modificada do PIC; o próprio PIC18F4550 como estudo de caso; e o vocabulário quantitativo de desempenho — tempo de execução, CPI, IPC, leis de Amdahl e Gustafson. Ao final, conectamos cada peça com as três tarefas do Projeto Integrador.
Arquitetura e Organização
A arquitetura de um sistema computacional é o conjunto de atributos visíveis ao programador: conjunto de instruções, registradores nomeáveis, modelo e modos de endereçamento de memória, mecanismos de exceção, tipos primitivos e modelo de E/S. É o contrato público entre quem escreve o código e quem projeta o chip. A organização, por sua vez, é o conjunto de decisões de implementação que realizam esse contrato: caminho de dados interno, profundidade de pipeline, hierarquia de cache, frequências, larguras físicas de barramento, tecnologia das memórias. Duas organizações distintas podem implementar a mesma arquitetura produzindo desempenhos, consumos e custos radicalmente diferentes, sem que um único programa correto consiga distingui-las pelo resultado lógico.
flowchart TB
P["Programador<br/>(código C, ASM)"]
A["Arquitetura<br/>ISA, registradores, modelo de memória,<br/>modos de endereçamento, exceções"]
O["Organização<br/>Pipeline, caches, ULA, barramentos,<br/>frequência, tecnologia de memória"]
H["Silício<br/>Transistores, geometria, materiais"]
P -->|"escreve programa"| A
A -.->|"contrato visível"| P
A --> O
O --> H
H -.->|"desempenho percebido"| P
A separação não é decorativa. Foi a IBM, em 1964, com a família System/360, quem primeiro definiu uma única arquitetura implementada por modelos com organizações deliberadamente distintas. O mesmo programa rodava em todos, dos modelos baratos aos topos de linha. Nasceu ali a noção moderna de compatibilidade de família.
Para uso prático, ofereço a prova do programador: se a alteração de um detalhe muda o resultado lógico de algum programa correto, esse detalhe é arquitetura; se só afeta tempo, consumo ou custo, é organização. Aplique ao Intel Core i9-13900K e ao Intel Core i3-13100. Ambos implementam x86-64 com SSE, AVX e AVX2; o mesmo binário roda em ambos com os mesmos registradores, instruções e modelo de memória virtual. Arquiteturalmente, equivalentes. A organização é radicalmente diferente: núcleos de desempenho e eficiência no i9, três níveis de cache com dezenas de megabytes, mais de 5 GHz, pipeline com mais de quinze estágios e dezenas de unidades de execução; o i3 tem menos de tudo. O mesmo benchmark roda muitas vezes mais rápido no i9 — eis a resposta da provocação inicial.
Antes de seguir, pergunte-se: se trocássemos a SRAM de 2 KB do PIC18F4550 por uma SRAM de 8 KB sem alterar o mapa de endereçamento visível ao programador, isso seria mudança de arquitetura ou de organização? Responder essa pergunta corretamente é o teste que valida se esta seção foi sedimentada.
Existe uma camada intermediária, a microarquitetura, que aparecerá nos Módulos 04 e 05. Ela vive dentro da organização: descreve como o processador implementa internamente a arquitetura — estrutura do pipeline, execução fora de ordem, forma da unidade de controle — sem descer ao nível de transistores. Cuidado com a literatura comercial: “arquitetura Intel Skylake” ou “arquitetura Apple M2” descrevem, no rigor que adotamos, microarquiteturas dentro das famílias x86-64 e ARMv8-A.
Cinco Gerações de Computadores
Não estudo história por erudição; estudo porque cada inovação arquitetural surgiu como resposta a uma limitação tecnológica concreta, e compreender a limitação é frequentemente a única forma de entender por que a decisão de projeto foi tomada como foi.
timeline
title Cinco gerações de computadores
1940-1955 : 1ª geração<br/>Válvulas termiônicas<br/>ENIAC, EDVAC<br/>programa armazenado
1955-1965 : 2ª geração<br/>Transistor discreto<br/>IBM 7090, PDP-1<br/>FORTRAN, COBOL
1965-1971 : 3ª geração<br/>CI SSI e MSI<br/>IBM System/360<br/>família arquitetural
1971-1980 : 4ª geração<br/>Microprocessador<br/>Intel 4004, 8086<br/>computador pessoal
1980-hoje : 5ª geração<br/>VLSI, computação ubíqua<br/>x86, ARM, PIC18<br/>RISC × CISC
A pré-história começa com a Máquina Analítica de Charles Babbage e com as notas de Ada Lovelace, em meados do século XIX, descrevendo o primeiro algoritmo escrito para um computador. A primeira geração (1940–1955) usa válvulas termiônicas: o ENIAC, com dezessete mil tubos, era programado por reconexão manual de cabos. Nessa geração, von Neumann formulou no relatório do EDVAC, em 1945, o conceito de programa armazenado.
A segunda geração trouxe os transistores discretos e linguagens como FORTRAN e COBOL. A terceira, dos anos 1960 ao início dos 1970, os circuitos integrados, o IBM System/360 e a microprogramação. A quarta começa em 1971 com o Intel 4004, primeiro microprocessador em pastilha única, e viabiliza o computador pessoal. A quinta geração, dos anos 1980 em diante, é a da integração em altíssima escala (VLSI) e da computação ubíqua: smartphone, sensor inteligente, microcontrolador embarcado. O PIC18F4550 — projetado pela Microchip em torno de 2007, ~30 000 transistores, 32 KB de Flash, 2 KB de RAM, dezenas de periféricos — é representante típico. Cada geração não substituiu as anteriores; aprimorou-as por uma camada adicional. O chip que você vai gravar executa internamente um modelo de Von Neumann modificado formulado em 1945.
Uma controvérsia das décadas de 1980 a 2000 merece destaque: RISC versus CISC. Processadores CISC (VAX-11, x86) adotavam conjuntos extensos de instruções e múltiplos modos de endereçamento, esperando reduzir o trabalho do compilador. Patterson em Berkeley e Hennessy em Stanford observaram empiricamente que só um pequeno subconjunto das instruções CISC era usado pelos compiladores; propuseram o contrário: poucas instruções simples, tamanho fixo, idealmente um ciclo cada, acesso a memória restrito a load e store. O PIC18F4550 é híbrido: tamanho fixo de 16 bits (típico RISC), mas 75 instruções, várias combinando acesso à memória com aritmética como ADDWF (típico CISC). O desfecho histórico foi pragmático: processadores x86 modernos traduzem internamente em micro-operações RISC; ARM e RISC-V acumularam extensões que os aproximam do CISC em densidade.
O Modelo de Von Neumann
A invenção que justifica nomear um modelo arquitetural inteiro em homenagem a John von Neumann foi a do programa armazenado: instruções representadas como números, armazenadas na mesma memória que armazena os dados, lidas pelo processador da mesma forma que dados são lidos. Essa decisão tem três consequências profundas: o programa pode ser modificado tão facilmente quanto qualquer outro dado, abrindo caminho para carregamento dinâmico de programas e compiladores; o hardware necessário para acessar instruções é o mesmo necessário para acessar dados, economizando circuitos; e abre-se a possibilidade — hoje desencorajada por segurança — de programas que se modificam a si mesmos.
flowchart LR
subgraph CPU["CPU"]
UC["Unidade<br/>de Controle"]
ULA["ULA"]
REG["Registradores<br/>PC, IR, ACC"]
end
MEM["Memória Principal<br/>instruções + dados<br/>(mesma)"]
IO["Subsistema<br/>de E/S"]
CPU <-->|"barramento único<br/>endereço/dado/controle"| MEM
CPU <--> IO
O modelo tem cinco subsistemas: unidade de controle, unidade lógica e aritmética, registradores de trabalho, memória principal e subsistema de E/S. A execução segue o ciclo de busca-decodificação-execução. O contador de programa (PC) contém o endereço da próxima instrução. A unidade de controle envia esse endereço pelo barramento; a memória responde com a instrução, que é colocada no registrador de instrução (IR); o IR é decodificado e os sinais correspondentes coordenam a execução. Ao final, o PC é incrementado (ou alterado por desvio) e o ciclo recomeça. É a base do funcionamento de quase todos os processadores que existiram, do EDVAC ao PIC18F4550 do seu kit.
A elegância do modelo esconde uma fragilidade. Como instruções e dados compartilham o mesmo barramento, a CPU não pode buscar uma instrução e ler um dado no mesmo instante: as duas operações precisam ser serializadas. Esse fenômeno, descrito por John Backus na sua palestra do prêmio Turing em 1977, ficou conhecido como gargalo de Von Neumann. Em primeira aproximação, se o tempo interno da CPU por instrução é Tp e o tempo de acesso à memória é Tm, o tempo efetivo por instrução satisfaz
\tau \geq \tau_p + 2 \, \tau_m,
porque cada instrução exige pelo menos uma busca da instrução em si e um acesso ao dado que ela manipula. Quando Tm é muito maior que Tp, a CPU passa a maior parte do tempo esperando a memória — situação que hoje atende pelo nome de memory wall. A solução clássica nos computadores de propósito geral foi a hierarquia de caches que você estudará no Módulo 09. A solução adotada pela maioria dos microcontroladores foi diferente e dá nome à próxima seção.
Harvard e Harvard Modificada
O nome “Harvard” provém do computador Mark I, construído por Howard Aiken em colaboração entre IBM e Universidade de Harvard, operacional em 1944. Usava fitas de papel perfurado para instruções e contadores eletromecânicos para dados, em sistemas fisicamente separados. Décadas depois, esse esquema histórico forneceu o nome para uma classe de processadores que mantém instruções e dados em memórias fisicamente distintas, com barramentos independentes.
flowchart LR
MI["Memória de<br/>Instruções<br/>(Flash 32 KB)"]
CPU["CPU"]
MD["Memória de<br/>Dados<br/>(SRAM 2 KB)"]
EE["EEPROM<br/>256 bytes"]
MI -->|"barramento de<br/>instruções 16 bits"| CPU
CPU <-->|"barramento de<br/>dados 8 bits"| MD
MI -.->|"TBLRD via TBLPTR<br/>(Harvard modificada)"| CPU
CPU <-.->|"EECON1/EECON2<br/>(protocolo)"| EE
O princípio é direto: instruções e dados ocupam espaços de endereçamento separados, com barramentos físicos próprios. Em um mesmo ciclo, o processador pode buscar uma instrução e simultaneamente ler ou escrever um dado. A largura dos dois barramentos pode ser diferente — no PIC18, instruções têm 16 bits e dados têm 8. As tecnologias de memória também podem diferir: Flash não volátil para instruções, SRAM volátil rápida para dados. A vantagem mais visível é o paralelismo de acesso. Processadores Harvard alcançam, em condições favoráveis, uma instrução executada por ciclo de relógio sem precisar de cache interna sofisticada.
A Harvard pura tem uma limitação prática: constantes e tabelas grandes — strings, tabelas de senos, vetores de calibração — não cabem confortavelmente na memória de dados. A solução do PIC18 e da maioria dos microcontroladores modernos é a Harvard modificada: mantém-se a separação de memórias e barramentos, mas a ISA inclui instruções específicas que permitem ler bytes da memória de instruções como se fossem dados, com latência ligeiramente maior. No PIC18, essas instruções são TBLRD*, TBLRD*+, TBLRD*- e TBLRD+*, operando com o ponteiro TBLPTR; o byte lido aparece em TABLAT. Ao declarar static const uint8_t tabela[] = { ... } em C, o XC8 aloca a tabela em Flash e emite automaticamente a sequência MOVLW/ADDWF/TBLRD* ao acessar tabela[i]. Para você, parece um acesso ordinário a vetor; para o hardware, é uma travessia de fronteira entre dois espaços de endereçamento que, em Harvard pura, seria proibida.
O PIC18 dispõe ainda de um terceiro espaço: a EEPROM de 256 bytes, para dados persistentes em pequena escala — calibrações, contadores não voláteis, identificadores únicos. O acesso é mediado por EECON1 e EECON2, com protocolo de bytes mágicos que evita escritas acidentais. A EEPROM tolera centenas de milhares de ciclos de escrita por célula, contra dezenas de milhares da Flash.
Três modelos lado a lado
| Aspecto | Von Neumann | Harvard pura | Harvard modificada (PIC18) |
|---|---|---|---|
| Espaços de endereçamento | Um único | Dois disjuntos | Dois (mais EEPROM), com acesso cruzado limitado |
| Barramentos físicos | Um | Dois independentes | Dois (instruções 16 bits, dados 8 bits) |
| Acesso paralelo busca/dado | Não | Sim | Sim |
| Constantes literais grandes | Vivem na RAM | Vivem na RAM (limitação) | Vivem em Flash, lidas via TBLRD |
| Programas auto-modificáveis | Possíveis | Impossíveis na ISA pura | Possíveis via escrita controlada na Flash |
| Representantes típicos | x86 (arquitetural), CPUs de propósito geral | DSPs clássicos | PIC, AVR, ARM Cortex-M, MSP430 |
Um ponto que costuma surpreender: o x86 moderno, do ponto de vista do programador, é uma arquitetura de Von Neumann — uma única memória vista pelo programa — mas internamente as caches L1 são separadas em cache de instruções e cache de dados, materializando uma organização de inspiração harvardiana. A arquitetura é Von Neumann; a organização é mista. Esse exemplo ilustra, sozinho, a potência da distinção que construímos na seção anterior.
O PIC18F4550 como Estudo de Caso
O microcontrolador foi escolhido por quatro razões. A regularidade ortogonal — 75 instruções, quase todas de um ciclo, codificação fixa de 16 bits em poucas famílias — permite estudar o ISA inteiro num único módulo. O acesso direto aos periféricos (ADC, USB, UART, SPI, I²C, comparadores, timers) integra-se ao chip e é documentado num único datasheet. A previsibilidade absoluta de timing, sem caches nem execução fora de ordem, deixa o tempo de execução exatamente calculável. Por fim, o custo e a robustez do KIT ACEPIC PRO V8.2 toleram os erros típicos de um estudante aprendendo eletrônica embarcada.
flowchart TB
subgraph Nucleo["Núcleo do PIC18F4550"]
UC["Unidade<br/>de Controle"]
ULA["ULA 8 bits"]
W["WREG"]
ST["STATUS<br/>C, DC, Z, OV, N"]
end
Flash["Flash 32 KB<br/>(16 K palavras<br/>de 16 bits)"]
SRAM["SRAM 2 KB<br/>(16 bancos +<br/>Access Bank)"]
EE["EEPROM<br/>256 bytes"]
subgraph Perif["Periféricos integrados"]
USB["USB 2.0"]
UART["EUSART"]
MSSP["MSSP<br/>(I²C/SPI)"]
ADC["ADC 10 bits<br/>13 canais"]
TIM["Timers<br/>+ CCP/PWM"]
IO["35 pinos I/O<br/>PORTA..PORTE"]
end
CK["Oscilador externo<br/>8 MHz → ÷4 → Fcy = 2 MHz<br/>+ PLL para USB"]
Flash --> Nucleo
Nucleo <--> SRAM
Nucleo <-.-> EE
Nucleo <--> Perif
CK --> Nucleo
A CPU abriga a unidade de controle, a ULA de 8 bits, o registrador WREG (W) e o registrador STATUS com os cinco flags fundamentais — Carry, Digit Carry, Zero, Overflow e Negative. A Flash de 32 KB armazena 16 K palavras de 16 bits, reprogramável in-circuit. A SRAM de 2 KB se organiza em dezesseis bancos de 256 bytes selecionados pelo BSR, com o Access Bank combinando os 96 bytes iniciais da RAM com os 160 bytes superiores onde residem os SFRs. Os periféricos são fartos: dois MSSP (I²C/SPI), EUSART, USB 2.0 full-speed, ADC de 10 bits com até 13 canais, três comparadores, quatro timers, dois módulos CCP e 35 pinos de I/O nos portos A, B, C, D e E.
Vamos exercitar a prova do programador sobre esse chip. O conjunto de 75 instruções, o WREG, os flags de STATUS, os três espaços de memória com seus mapeamentos e os SFRs como TRISx, LATx, ADCONx — tudo isso é arquitetura, porque o seu código depende deles. O pipeline de dois estágios, o Access Bank, a largura física do barramento de dados de 8 bits, o cristal externo e a divisão por quatro, a PLL do USB, a tecnologia das células de memória — tudo isso é organização. Esse quadro é a base da Tarefa 2 do Projeto Integrador.
O ciclo de instrução do PIC18 compacta as cinco fases conceituais — busca, decodificação, execução, acesso à memória e escrita do resultado — em apenas dois estágios físicos: fetch e execute. É a arquitetura Harvard que permite essa compactação, pois a CPU consegue buscar a próxima instrução pelo barramento de instruções enquanto a atual ainda está sendo executada via barramento de dados.
flowchart LR
subgraph C1["Ciclo 1"]
F1["Fetch I1"]
end
subgraph C2["Ciclo 2"]
E1["Execute I1"]
F2["Fetch I2"]
end
subgraph C3["Ciclo 3"]
E2["Execute I2"]
F3["Fetch I3"]
end
subgraph C4["Ciclo 4"]
E3["Execute I3"]
F4["Fetch I4"]
end
F1 --> E1
F2 --> E2
F3 --> E3
F4 --> E4["Execute I4"]
Depois de uma latência inicial de um ciclo, o processador conclui uma instrução por ciclo de máquina. A exceção fica por conta das instruções que alteram o PC — todas as variantes de GOTO, CALL, BRA quando tomadas, RETURN, RETFIE e os testes condicionais BTFSS/BTFSC que causam skip. Nessas, o prefetch que já trouxe a instrução errada precisa ser descartado, custando um ciclo adicional. Em outras palavras, o CPI dessas instruções é 2 em vez de 1. Essa única observação — desvios tomados custam dois ciclos enquanto a maioria custa um — sustenta toda a análise de desempenho que você fará neste semestre.
Métricas de Desempenho
Quando alguém afirma que um processador é “duas vezes mais rápido” do que outro, pergunte: em que carga de trabalho, com qual compilador, sob qual frequência e medindo qual quantidade? Sem qualificação, a expressão é vazia. Esta seção fornece o vocabulário para conversar sobre desempenho com precisão.
A métrica fundamental é o tempo de execução — duração em segundos entre o início e a conclusão da tarefa. Decompomos esse tempo em três fatores:
T = N_{\text{instr}} \cdot \text{CPI} \cdot T_{cy},
onde N é o número total de instruções executadas, CPI é o número médio de ciclos de máquina por instrução (Cycles Per Instruction) e Tcy é o período de um ciclo de máquina. A elegância da decomposição está em que cada fator depende de uma camada distinta: N depende do algoritmo e do compilador; CPI depende da arquitetura, da microarquitetura e da mistura de instruções; Tcy depende exclusivamente da organização — tecnologia, projeto físico, refrigeração. Otimizar desempenho é atacar simultaneamente esses três fatores.
flowchart LR
XT["Cristal externo<br/>Fosc = 8 MHz"]
DIV["Divisor<br/>÷ 4"]
FCY["Clock de máquina<br/>Fcy = 2 MHz<br/>Tcy = 500 ns"]
CPU["CPU<br/>(Q1, Q2, Q3, Q4)"]
XT --> DIV --> FCY --> CPU
No PIC18, há um detalhe que você precisa memorizar com firmeza: o cristal externo gera a frequência Fosc, mas internamente um divisor por quatro produz o clock de máquina Fcy. A relação é Fcy = Fosc / 4. Para o cristal de 8 MHz típico do KIT ACEPIC PRO V8.2, Fcy vale 2 MHz, e portanto Tcy = 500 ns. A confusão mais frequente nesta disciplina é trocar Fosc por Fcy; ao errar essa divisão por quatro, você produz uma previsão teórica quatro vezes menor do que o valor medido. Anote esse fator: ele será aplicado em todas as análises de tempo do semestre.
Para uma mistura de instruções com frequências relativas f1, f2, …, fn e CPIs individuais c1, c2, …, cn, o CPI médio vale
\text{CPI}_{\text{médio}} = \sum_{i=1}^{n} f_i \, c_i.
Exemplo concreto: um programa com 90% de instruções de CPI 1 e 10% de desvios tomados (CPI 2) tem CPI médio 0,90 · 1 + 0,10 · 2 = 1,10. A 8 MHz com 10 000 instruções, o tempo total é 10 000 · 1,10 · 500 ns ≈ 5,5 ms.
O throughput generaliza a métrica para sistemas com muitas tarefas em paralelo. Em processadores, mede-se em IPC (Instructions Per Cycle) ou MIPS. A relação é IPC = 1/CPI. No PIC18, em código sem desvios tomados, o IPC fica próximo de 1 em regime de pipeline cheio. Superescalares modernos atingem IPC maior que 1 — tema do Módulo 15. As métricas MIPS e MFLOPS têm armadilha: dependem da mistura de instruções, e uma máquina com mais instruções “fáceis” pode apresentar MIPS maior sem ser, em sentido prático, mais rápida.
Para tornar o efeito do CPI palpável, considere o seguinte laço de soma — variantes desse padrão reaparecerão ao longo do semestre.
01_cpi_demo.c
/*
* Modulo 01 - Demonstracao do conceito de CPI (Cycles Per Instruction).
* Plataforma: PIC18F4550 + KIT ACEPIC PRO V8.2.
*
* Duas funcoes realizam exatamente a mesma soma de 100 valores armazenados
* em um vetor, mas com numero de instrucoes e mistura de CPIs diferentes.
* Compilando com XC8 e inspecionando o .lst, voce conta as instrucoes
* geradas e calcula o CPI medio de cada versao. Esse exercicio fixa a
* relacao T = N_instr * CPI_medio * Tcy estudada no capitulo.
*/
#include <xc.h>
#include <stdint.h>
#pragma config FOSC = XT_XT
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config MCLRE = ON
#pragma config PBADEN = OFF
#define _XTAL_FREQ 8000000UL
static const uint8_t valores[100] = {
/* 100 bytes constantes; ficam na Flash, lidos via TBLRD */
1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30, 31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50, 51,52,53,54,55,56,57,58,59,60,
61,62,63,64,65,66,67,68,69,70, 71,72,73,74,75,76,77,78,79,80,
81,82,83,84,85,86,87,88,89,90, 91,92,93,94,95,96,97,98,99,100
};
static volatile uint16_t soma;
/* Versao 1: laço explícito. Muitas instruções de controle (incremento
* de índice, comparacao, desvio condicional). CPI medio mais alto pelo
* peso dos desvios tomados. */
static void soma_v1(void)
{
soma = 0;
for (uint8_t i = 0; i < 100; i++) {
soma = (uint16_t)(soma + valores[i]);
}
}
/* Versao 2: laco desenrolado em blocos de 4. Reduz a fracao de instrucoes
* de controle e, em consequencia, derruba o CPI medio mesmo executando
* praticamente o mesmo numero de somas. */
static void soma_v2(void)
{
soma = 0;
for (uint8_t i = 0; i < 100; i += 4) {
soma = (uint16_t)(soma + valores[i]);
soma = (uint16_t)(soma + valores[i + 1]);
soma = (uint16_t)(soma + valores[i + 2]);
soma = (uint16_t)(soma + valores[i + 3]);
}
}
void main(void)
{
TRISD = 0x00;
LATD = 0x00;
while (1) {
LATDbits.LATD0 = 1;
soma_v1();
LATDbits.LATD0 = 0;
__delay_ms(20);
LATDbits.LATD1 = 1;
soma_v2();
LATDbits.LATD1 = 0;
__delay_ms(200);
}
}01_cpi_demo.asm
; Modulo 01 - Demonstracao de CPI em assembly puro.
; Plataforma: PIC18F4550 + ACEPIC PRO V8.2.
;
; O programa realiza dois trechos de codigo equivalentes em efeito,
; mas com numero distinto de instrucoes e CPI medio distinto. RD0
; delimita o trecho 1 (laço); RD1 delimita o trecho 2 (desenrolado).
; Medindo a largura dos pulsos com osciloscopio, o estudante observa
; diretamente a relacao T = N_instr * CPI_medio * Tcy.
LIST P=18F4550
#include <p18f4550.inc>
CONFIG FOSC=XT_XT, WDT=OFF, LVP=OFF, MCLRE=ON, PBADEN=OFF
CBLOCK 0x00
ACC_L
ACC_H
IDX
D1, D2, D3
ENDC
ORG 0x0000
GOTO INICIO
ORG 0x0040
INICIO:
CLRF TRISD, ACCESS
CLRF LATD, ACCESS
LOOP_PRINC:
; --- Trecho 1: laço de 100 incrementos ----------------------
BSF LATD, 0, ACCESS
CLRF ACC_L, ACCESS
CLRF ACC_H, ACCESS
MOVLW d'100'
MOVWF IDX, ACCESS
LACO_V1:
MOVLW 1
ADDWF ACC_L, F, ACCESS
MOVLW 0
ADDWFC ACC_H, F, ACCESS
DECFSZ IDX, F, ACCESS
BRA LACO_V1
BCF LATD, 0, ACCESS
CALL DELAY_CURTO
; --- Trecho 2: laço desenrolado em blocos de 4 --------------
BSF LATD, 1, ACCESS
CLRF ACC_L, ACCESS
CLRF ACC_H, ACCESS
MOVLW d'25' ; 25 iteracoes de 4 somas cada
MOVWF IDX, ACCESS
LACO_V2:
MOVLW 1
ADDWF ACC_L, F, ACCESS
ADDWFC ACC_H, F, ACCESS
MOVLW 1
ADDWF ACC_L, F, ACCESS
ADDWFC ACC_H, F, ACCESS
MOVLW 1
ADDWF ACC_L, F, ACCESS
ADDWFC ACC_H, F, ACCESS
MOVLW 1
ADDWF ACC_L, F, ACCESS
ADDWFC ACC_H, F, ACCESS
DECFSZ IDX, F, ACCESS
BRA LACO_V2
BCF LATD, 1, ACCESS
CALL DELAY_LONGO
BRA LOOP_PRINC
DELAY_CURTO:
MOVLW d'10'
MOVWF D3, ACCESS
DC_3:
MOVLW d'200'
MOVWF D2, ACCESS
DC_2:
MOVLW d'20'
MOVWF D1, ACCESS
DC_1:
DECFSZ D1, F, ACCESS
BRA DC_1
DECFSZ D2, F, ACCESS
BRA DC_2
DECFSZ D3, F, ACCESS
BRA DC_3
RETURN
DELAY_LONGO:
MOVLW d'100'
MOVWF D3, ACCESS
DL_3:
MOVLW d'200'
MOVWF D2, ACCESS
DL_2:
MOVLW d'20'
MOVWF D1, ACCESS
DL_1:
DECFSZ D1, F, ACCESS
BRA DL_1
DECFSZ D2, F, ACCESS
BRA DL_2
DECFSZ D3, F, ACCESS
BRA DL_3
RETURN
ENDAs duas versões fazem o mesmo trabalho útil, mas a fração de instruções de controle é diferente, e portanto o CPI médio difere entre elas. Medindo a largura do pulso em RD0 com osciloscópio, você confirma diretamente a previsão teórica calculada a partir do datasheet.
A Lei de Amdahl responde a uma pergunta natural: suponha que você identifique, em um programa, uma fração f do tempo de execução que pode ser acelerada por um fator k. Qual o ganho global S, ou speedup, que se obtém? A resposta foi formulada por Gene Amdahl em 1967:
S = \frac{1}{(1-f) + \dfrac{f}{k}}.
No limite em que k tende ao infinito, o speedup máximo vale 1 dividido por 1 menos f, independentemente de quão rápida a porção acelerada se torne.
flowchart LR
T0["Tempo original<br/>T = 1"]
F["Fração f<br/>acelerável"]
NF["Fração (1−f)<br/>inalterada"]
K["Aceleração<br/>por fator k"]
S["Speedup global<br/>S = 1 / ((1−f) + f/k)"]
LIM["Limite k → ∞<br/>S_max = 1 / (1−f)"]
T0 --> F
T0 --> NF
F --> K
K --> S
NF --> S
S --> LIM
A lição é desconcertante. Acelerar infinitamente 90% do tempo produz speedup global de apenas dez vezes, porque os 10% restantes continuam consumindo o mesmo tempo absoluto. Acelerar por fator 10 uma fração de 50% do tempo dá speedup de apenas 1,82 vezes, não 5,5. Otimização vale a pena onde o programa gasta tempo, e o ganho global é tetado pela fração não otimizada. Antes de qualquer otimização, pergunte: que fração do tempo total essa rotina consome?
A Lei de Gustafson, de 1988, complementa Amdahl. Na prática, à medida que as máquinas ficam mais rápidas, os usuários aumentam o tamanho dos problemas, e a fração paralelizável cresce mais que a sequencial. Sob essa hipótese, o speedup escalado vale (1 − f) + k · f, crescendo linearmente em k. Não há contradição: Amdahl descreve strong scaling (problema fixo, mais recursos); Gustafson, weak scaling (problema cresce com os recursos). Para sistemas embarcados, em que o problema é dado pela aplicação — controlar um motor, ler um sensor a taxa fixa — Amdahl é mais relevante. Para data centers, Gustafson costuma ser mais aderente.
Se um programa gasta 30% do tempo em E/S sequencial obrigatória e 70% em um laço puramente computacional, qual o speedup máximo teórico possível mesmo paralelizando o laço infinitamente? Faça a conta antes de avançar; este é exatamente o tipo de pergunta que aparecerá em prova.
Da Teoria ao Kit: o Projeto Integrador
A teoria serviria de pouco sem aferição empírica. A cadeia de ferramentas que conecta o seu código C ao silício é uma sequência de cinco blocos.
flowchart LR
SRC["Código-fonte<br/>(.c / .asm)<br/>MPLAB X IDE"]
XC8["XC8 (C)<br/>ou pic-as (ASM)"]
LINK["Linker<br/>(.elf)"]
HEX["Imagem<br/>Intel HEX (.hex)"]
CHIP["PIC18F4550<br/>(Flash gravada)"]
SRC --> XC8 --> LINK --> HEX
HEX -->|"bootloader<br/>via USB"| CHIP
HEX -->|"PICkit<br/>via ICSP"| CHIP
O MPLAB X IDE é o ambiente integrado. O XC8 traduz código C em assembly e código de máquina; o pic-as faz o mesmo a partir de assembly puro. A saída é um .hex no formato Intel HEX, gravado na Flash pelo bootloader residente (via USB com AN1310 ou ACEPIC Terminal) ou por um PICkit conectado ao header ICSP.
A primeira tarefa do Projeto Integrador pede configurar todo esse ambiente e gravar um programa que pisca os oito LEDs do kit em sequência. O programa é simples — PORTD como saída, varredura L1 a L8 com 500 ms entre eles — mas o ponto pedagógico está na cadeia inteira que precisa funcionar: driver do CH340G, versão compatível do MPLAB X, XC8 reconhecendo o PIC18F4550, jumpers na posição correta, bootloader íntegro. Cada elo é uma oportunidade de falha, e lidar com elas é parte da experiência do módulo.
01_blink_leds.c
/*
* Modulo 01 - Tarefa 1: programa de validacao do ambiente.
* Plataforma: PIC18F4550 + KIT ACEPIC PRO V8.2.
* Cristal externo de 8 MHz (XT) -> Fosc = 8 MHz, Tcy = 0,5 us.
* LEDs L1..L8 ligados em PORTD (RD0..RD7); jumper LEDS = fechado.
* Compilador: Microchip XC8.
*/
#include <xc.h>
// Configuracao de fusiveis para o cristal de 8 MHz do kit, WDT desligado,
// LVP desligado, MCLR habilitado, brown-out desligado.
#pragma config FOSC = XT_XT
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config MCLRE = ON
#pragma config PBADEN = OFF // PORTB em modo digital apos reset
#define _XTAL_FREQ 8000000UL
static void delay_500ms(void)
{
// Atraso baseado em macro do XC8, equivalente a 500 ms.
__delay_ms(500);
}
void main(void)
{
TRISD = 0x00; // PORTD inteiro como saida (LEDs L1..L8)
LATD = 0x00;
while (1) {
// Acende um LED de cada vez, varrendo da direita para a esquerda.
for (unsigned char i = 0; i < 8; i++) {
LATD = (unsigned char)(1u << i);
delay_500ms();
}
LATD = 0x00;
delay_500ms();
}
}01_blink_leds.asm
; Modulo 01 - Tarefa 1: programa de validacao do ambiente em assembly.
; Plataforma: PIC18F4550 + KIT ACEPIC PRO V8.2.
; Cristal externo 8 MHz, Tcy = 0,5 us.
; LEDs L1..L8 em RD0..RD7.
; Montador: MPASMX / pic-as (sintaxe MPASM classica).
LIST P=18F4550
#include <p18f4550.inc>
CONFIG FOSC=XT_XT, WDT=OFF, LVP=OFF, MCLRE=ON, PBADEN=OFF
; ---------------- Variaveis em Access Bank ----------------
CBLOCK 0x00
MASK ; padrao atual dos LEDs
D1, D2, D3 ; contadores do atraso
ENDC
; ---------------- Vetor de reset ----------------
ORG 0x0000
GOTO INICIO
ORG 0x0020
INICIO:
CLRF TRISD, ACCESS ; PORTD todo como saida
CLRF LATD, ACCESS
MOVLW 0x01
MOVWF MASK, ACCESS
LOOP_PRINC:
MOVF MASK, W, ACCESS
MOVWF LATD, ACCESS
CALL DELAY_500MS
; Desloca a mascara um bit para a esquerda.
RLNCF MASK, F, ACCESS
; Se completou 8 posicoes, recomeca com 0x01.
MOVF MASK, W, ACCESS
BNZ LOOP_PRINC
MOVLW 0x01
MOVWF MASK, ACCESS
BRA LOOP_PRINC
; ---------------- Atraso por software ----------------
; Aproximadamente 500 ms a Tcy = 0,5 us.
; Loop triplo: D3 * D2 * D1 ciclos = 250 * 200 * 20 ~ 1e6 ciclos -> 500 ms.
DELAY_500MS:
MOVLW d'250'
MOVWF D3, ACCESS
DL_3:
MOVLW d'200'
MOVWF D2, ACCESS
DL_2:
MOVLW d'20'
MOVWF D1, ACCESS
DL_1:
DECFSZ D1, F, ACCESS
BRA DL_1
DECFSZ D2, F, ACCESS
BRA DL_2
DECFSZ D3, F, ACCESS
BRA DL_3
RETURN
ENDA segunda tarefa pede evidência empírica da Harvard modificada. O programa define uma tabela de oito padrões binários em const uint8_t (alocada em Flash pelo XC8) e uma variável de índice em RAM; a cada iteração, o byte é lido via TBLRD e escrito em LATD. O entregável exige o quadro Arq/Org dos elementos do PIC18F4550 e a inspeção do arquivo .map gerado pelo XC8: ele lista os endereços de cada símbolo, e você verá padroes[] em endereços baixos (Flash, a partir de 0x000000) enquanto a variável de índice ocupa outra faixa (RAM, no Access Bank). Essa observação cristaliza a separação que, no diagrama, parecia apenas conceitual.
A terceira tarefa fecha o ciclo teoria-medição. Você grava um programa que eleva RD0 antes de um trecho instrumentado e o abaixa depois, observa o pulso no osciloscópio, calcula o tempo teórico pela contagem de instruções no datasheet e compara com a medida. O entregável inclui tabela de instruções, cálculo teórico, valor medido, erro relativo e discussão das fontes de incerteza. É a primeira tarefa em que o estudante deixa de ser usuário do microcontrolador e começa a agir como engenheiro: prevê, mede, confronta.
Configuração física do kit: pontos críticos
Quatro pontos do KIT ACEPIC PRO V8.2 merecem atenção logo no primeiro módulo. O jumper LEDS deve estar fechado para que L1 a L8 acendam quando LATD for escrito. O jumper J1 deve estar na posição 1-2, alimentando o chip com 5 V regulados. Os jumpers JP14 do cristal de 8 MHz devem ficar fechados na posição vertical; abertos, o chip recorre ao oscilador interno e o programa parece rodar em frequência errada. O DIP-switch DP2 deve ter as chaves 5 a 8 em OFF, pois controlam os cátodos dos displays de 7 segmentos compartilhados com PORTD. Esses detalhes parecem triviais, mas consomem uma aula inteira quando ignorados.
Há ainda o diário de projeto, um arquivo em Markdown na pasta /docs/diario/ da dupla, atualizado a cada tutoria com data, presentes, atividades, problemas, soluções e planejamento da semana seguinte. Cumpre três funções: continuidade entre sessões, materialização do aprendizado pelo ato de escrever e evidência para a avaliação contínua.
Síntese
Você percorreu, neste módulo, um caminho que partiu de uma distinção aparentemente acadêmica — arquitetura versus organização — e desembocou na configuração física de um microcontrolador real, com tempo de execução medido no osciloscópio. A trajetória passou pela história em cinco gerações, pela controvérsia RISC versus CISC, pelos modelos Von Neumann e Harvard (com a variante modificada do PIC18) e pelas métricas que sustentarão as análises de desempenho dos próximos quatorze módulos.
A pergunta de abertura — como dois processadores com a mesma arquitetura podem diferir cinco vezes em desempenho? — agora tem resposta precisa: arquitetura define o contrato com o programador, organização define o desempenho real. Essa é a tese central do nosso campo e o pré-requisito para tudo que vem adiante.
Antes de virar a página, faça uma autoavaliação honesta. Você deve enunciar as definições operacionais de arquitetura e organização e aplicar a prova do programador a qualquer componente. Deve descrever o ciclo de busca-decodificação-execução e identificar o gargalo de Von Neumann em uma frase. Deve diferenciar Harvard pura e modificada e justificar a escolha da segunda para o PIC18F4550. Deve calcular o tempo de execução pela equação fundamental T igual a N vezes CPI vezes Tcy, sem esquecer da divisão por quatro no clock do PIC18. Deve aplicar a Lei de Amdahl para estimar o speedup máximo em cenários típicos. E deve ter, no diário de projeto, o relato da primeira sessão de tutoria com o LED piscando, o quadro Arq/Org e o pulso medido no osciloscópio.
No Módulo 02, mergulharemos na representação de dados e aritmética computacional: complemento de dois, IEEE 754, circuitos somadores e multiplicadores. Deixo uma pergunta para o intervalo: por que somar dois números de 16 bits em um chip de 8 bits como o PIC18 exige mais de uma instrução, e que registrador especial entra em cena nessa segunda instrução?
Antes da próxima aula, abra o MPLAB X, compile o programa do LED piscando que apresentei nas Tarefas do Projeto Integrador, conecte o osciloscópio em RD0 e meça a largura do pulso. Compare com o valor que você calcular a partir da contagem de instruções e da relação Tcy igual a 4 sobre Fosc. O encontro entre teoria e medição é o ponto de partida do seu trabalho no semestre inteiro.