flowchart TD
OBJ["Pergunta inicial:<br/>como ADDWF cabe em<br/>4 pulsos de clock?"] --> ALU["ALU<br/>motor combinacional"]
OBJ --> REG["Banco de<br/>registradores<br/>WREG + File Register"]
OBJ --> BUS["Barramentos e<br/>multiplexadores"]
ALU --> CIC["Ciclo de instrução<br/>fases + prefetch"]
REG --> CIC
BUS --> CIC
CIC --> PIC["Caminho de dados<br/>do PIC18F4550<br/>estados Q1 a Q4"]
PIC --> QUANT["Análise quantitativa<br/>CPI e Amdahl"]
QUANT --> PI["Projeto Integrador:<br/>WREG, tabela de ciclos,<br/>fases observáveis"]
Módulo 04: Unidade Central de Processamento — Caminho de Dados — Resumo
Este é o resumo de revisão do Módulo 04. Reúno aqui, em prosa enxuta, o essencial do 04_material.qmd para você reler na véspera da avaliação e antes das sessões de tutoria do Projeto Integrador. O tratamento aprofundado, com demonstrações e exemplos suplementares, está em 04_material.qmd e em 04_livro.qmd.
Uma única instrução ADDWF SOMA, F, A precisa, em quatro pulsos do oscilador, buscar a próxima instrução, decodificar a atual, ler o operando, somar com WREG, atualizar STATUS e gravar o resultado. Como cinco operações conceituais cabem em quatro tiquetaques? Mantenha a pergunta na cabeça enquanto percorremos a ALU, o banco de registradores, os barramentos, o ciclo de instrução, o modelo monociclo e a análise quantitativa que fecha o módulo.
A Unidade Lógica e Aritmética
A ALU é o componente puramente combinacional responsável por todas as operações aritméticas e lógicas do processador. A cada ciclo recebe dois operandos A e B de n bits e um vetor de sinais de controle, devolvendo um resultado R e um vetor de flags. Formalmente é uma função \text{ALU}: \{0,1\}^n \times \{0,1\}^n \times \{0,1\}^k \to \{0,1\}^n \times \{0,1\}^f. No PIC18F4550 temos n=8 e o repertório inclui soma e subtração com e sem carry, AND, OR, XOR, NOT, deslocamentos com e sem rotação pelo carry, incremento e decremento.
O tijolo elementar é o somador completo de um bit. Encadeando oito deles em série obtém-se um somador ripple-carry de oito bits, no qual o carry propaga-se serialmente do bit menos significativo ao mais significativo. Esse atraso cabe folgadamente num ciclo de máquina do PIC18; em CPUs de 32 ou 64 bits, exige técnicas como carry-lookahead. Sobre essa base aritmética monta-se a parte lógica com arranjos paralelos de portas e os deslocamentos com um barrel shifter. Todos os subcircuitos operam em paralelo a partir dos mesmos operandos e um multiplexador na saída escolhe o resultado válido. O caminho crítico é o do subcircuito mais lento, em geral o somador.
flowchart LR
A["Operando A<br/>8 bits"] --> SOM["Somador<br/>8 bits"]
B["Operando B<br/>8 bits"] --> SOM
A --> LOG["Bloco lógico<br/>AND OR XOR NOT"]
B --> LOG
A --> SHF["Shifter<br/>esquerda/direita"]
A --> INC["Incremento<br/>Decremento"]
SOM --> MUX["MUX de saída"]
LOG --> MUX
SHF --> MUX
INC --> MUX
CTRL["Sinais de controle<br/>seletor s"] --> MUX
MUX --> R["Resultado R"]
SOM --> F["Flags<br/>Z C DC OV N"]
A ALU produz, além do resultado, cinco flags armazenados no registrador STATUS: Z (zero), C (carry), DC (digit carry, útil em BCD), OV (overflow em complemento de dois) e N (negativo). Esses flags cumprem papel duplo. São consultados pelas instruções de desvio condicional como BZ ou BNC, base da lógica de fluxo em assembly. E registram informação que se perderia: o carry produzido por uma soma de bytes é o único modo de implementar somas de múltipla precisão encadeando ADDWFC.
Se uma operação AND simplesmente não envolve o somador, qual deve ser a regra de atualização do carry numa instrução ANDWF? Sua resposta antecipa por que somas mexem em Z, C, DC, OV e N, enquanto um AND mexe apenas em Z e N.
A assimetria não é caprichosa. O AND não produz carry nem digit carry e não tem sentido em complemento de dois; manter os valores antigos preserva informação útil. Esse comportamento decorre diretamente da estrutura física — o MUX de saída de flags não roteia C, DC e OV quando a operação não envolveu o somador.
O Banco de Registradores
Por que processadores têm registradores em vez de operarem diretamente sobre a RAM? Memórias rápidas são caras por bit e ocupam área expressiva; memórias baratas são lentas. Se cada operando viesse da RAM, o ciclo seria dominado pelo tempo de acesso. A solução é manter, dentro do processador, um conjunto pequeno de células de altíssima velocidade fabricadas com a mesma tecnologia das portas lógicas da ALU. Esses registradores, organizados em um banco, são os operandos preferenciais das instruções aritméticas.
Um banco com r registradores de n bits possui p_r portas de leitura e p_w portas de escrita. Em RISC clássico, add r1, r2, r3 exige duas leituras simultâneas; daí a convenção de duas portas de leitura e uma de escrita. Cada porta adicional custa MUXes, fios, área, consumo e atraso.
O PIC18F4550 não segue esse modelo simétrico. Herda da família PIC original um único acumulador chamado WREG e um amplo espaço de RAM rápida chamado File Register, que abriga tanto registradores de função especial (SFR) quanto a área de uso geral. A maior parte das instruções aritméticas e lógicas tem a forma OP f, d, a: f é o endereço de um registrador no File Register, d indica se o resultado vai para WREG (0) ou para o próprio f (1), e a seleciona entre o Access Bank e o banco apontado pelo BSR. Um operando é implícito — WREG — e o outro explícito — f. Como WREG é sempre um dos operandos, não é preciso codificar seu endereço, e isso economiza bits no formato da instrução.
flowchart LR
WREG["WREG<br/>acumulador 8 bits"] --> ALU
FR["File Register<br/>SRAM endereçável<br/>até 4096 posições"] --> ALU["ALU 8 bits"]
ALU -->|"d=0"| WREG
ALU -->|"d=1"| FR
AB["Access Bank<br/>96 posições rápidas<br/>sem ajustar BSR"] -.fatia rápida.-> FR
BSR["BSR<br/>seleciona banco"] --> FR
O banco é fisicamente assimétrico. WREG é independente, ligado a uma das portas da ALU. O File Register é uma SRAM dual-port que serve simultaneamente de operando e destino: uma ADDWF f, 1, A lê f, soma com WREG e devolve o resultado a f num único ciclo de máquina. O Access Bank, selecionado quando o bit a vale 0, mapeia as primeiras posições da memória de dados sem exigir configuração do BSR — e o XC8 aloca nele, por padrão, as variáveis mais frequentes. Cada variável fora do Access Bank exige um MOVLB extra, e o custo se acumula em laços apertados. A primeira tarefa do Projeto Integrador pede exatamente isso: minimizar acessos à RAM mantendo dados frequentes em WREG e no Access Bank.
Barramentos e Multiplexadores
Barramentos são as estradas internas pelas quais os dados trafegam entre componentes. Em qualquer instante, um barramento de n bits transporta uma única palavra. No PIC18F4550 a via interna de dados tem oito bits e a via de instruções tem dezesseis, refletindo o tamanho fixo da maioria das instruções — assimetria típica das arquiteturas Harvard, em contraste com a Von Neumann pura.
Apenas uma fonte pode dirigir um barramento a cada ciclo; duas fontes ativas simultaneamente produzem curto interno potencialmente destrutivo. Em projetos modernos dentro de um chip, os tradicionais buffers tri-state cedem lugar a multiplexadores. Um MUX escolhe entre m entradas qual será conectada à saída segundo um sinal de seleção. Aparece antes de cada porta da ALU, antes de cada porta de escrita do banco e antes do contador de programa, para decidir entre PC incrementado, alvo de desvio incondicional e alvo de desvio condicional.
A latência de um MUX é pequena: mesmo um caminho com cinco ou seis MUXes em série tem atraso inferior ao de uma única passagem pelo somador. Por isso os projetistas usam-nos com generosidade. Essa onipresença justifica a complexidade da unidade de controle no Módulo 05: cada MUX exige seu próprio seletor. O caminho de dados é o mapa rodoviário; o vetor de controle é o roteiro de viagem.
O Ciclo de Instrução
Toda instrução atravessa um ciclo composto por fases distintas. A nomenclatura clássica de Hennessy e Patterson decompõe em cinco: busca (IF), decodificação (ID), execução (EX), acesso à memória (MEM) e escrita do resultado (WB). O PIC18F4550 não implementa explicitamente as cinco — esse modelo é mais útil para RISC com pipeline pleno, tema do Módulo 06. O PIC18 adota uma estrutura simplificada de duas fases sobrepostas via prefetch: em qualquer ciclo de máquina, executa a instrução n e busca a n+1 simultaneamente. Essa sobreposição é viável porque memória de programa e memória de dados são fisicamente separadas (Harvard) e os dois acessos usam caminhos independentes que não competem por recursos.
flowchart LR
subgraph C1["Ciclo n"]
F1["Fetch I1"]
end
subgraph C2["Ciclo n+1"]
F2["Fetch I2"]
E1["Execute I1"]
end
subgraph C3["Ciclo n+2"]
F3["Fetch I3"]
E2["Execute I2"]
end
subgraph C4["Ciclo n+3<br/>desvio tomado"]
FX["Fetch descartado"]
E3["Execute I3"]
end
Em consequência, a maior parte das instruções consome exatamente um ciclo de máquina, equivalente a quatro pulsos do oscilador. Os desvios efetivamente tomados são a exceção: a instrução pré-buscada torna-se inútil porque o fluxo salta para outro endereço, exigindo descartar a busca já feita e iniciar outra — daí o custo de dois ciclos. É a manifestação concreta do hazard de controle que generalizaremos no Módulo 07.
Cada ciclo de máquina subdivide-se em quatro estados Q. Q1 amostra o PC e ativa a leitura da memória de programa. Q2 amostra o IR e dispara a leitura do operando no File Register. Q3 é o pico da execução, com a ALU produzindo resultado e flags. Q4 escreve o destino e captura os flags em STATUS. Os estados Q se sobrepõem entre instruções consecutivas: enquanto a instrução n está em Q3 e Q4, a n+1 está em Q1 e Q2. Eis o segredo da pergunta de abertura: as cinco operações conceituais não cabem em quatro pulsos se executadas em sequência, mas cabem quando busca, decodificação, leitura, computação e escrita se intercalam entre instruções vizinhas.
A terceira tarefa do Projeto Integrador pede que você evidencie experimentalmente as fases acionando pinos do PORTB antes e depois de classes específicas de instruções e medindo, com analisador lógico, as larguras de pulso. A diferença entre uma instrução ALU monociclo e um desvio tomado é nitidamente distinguível. O trecho abaixo, núcleo dessa medição, será o ponto de partida da prática.
Atenção metodológica: as próprias instruções de marcação bsf e bcf sobre LATB consomem um ciclo cada e entram na largura medida. Em medições rigorosas é preciso descontar esse custo, como se desconta a tara do recipiente antes de pesar a substância.
O Caminho de Dados Monociclo
Apresento o caminho de dados monociclo como construção pedagógica. Nesse modelo, cada instrução completa todas as fases num único ciclo de clock, cujo período precisa acomodar o caminho mais lento — tipicamente uma carga, que envolve leitura da memória de dados como passo terminal. Formalmente, T_{clk} = \max_\iota \mathcal{L}(\iota), em que \mathcal{L}(\iota) é a latência combinacional da instrução \iota. A desvantagem é evidente: instruções simples gastam o mesmo tempo das mais complexas, ineficiência proibitiva em programas reais. CPUs reais adotam multiciclo ou pipeline. O monociclo aparece aqui pela clareza didática: separação entre caminho de dados e controle, papel dos MUXes, organização do PC e distinção entre instruções tipo R, tipo I e de desvio reaparecem, com refinamentos, em todos os modelos mais sofisticados.
flowchart LR
PC["PC"] --> MP["Memória<br/>de programa"]
MP --> IR["IR"]
IR --> DEC["Decodificador"]
DEC --> BR["Banco de<br/>registradores"]
BR -->|"porta A"| ALU["ALU"]
BR -->|"porta B"| MUX1(("MUX"))
IMM["Imediato<br/>sinal-estendido"] --> MUX1
MUX1 --> ALU
ALU --> MUX2(("MUX"))
ALU --> MD["Memória<br/>de dados"]
MD --> MUX2
MUX2 --> BR
ALU -.flags.-> PCNXT["Próximo PC"]
PCNXT --> PC
Lendo a topologia da esquerda para a direita: o PC alimenta o endereço da memória de instruções; a saída segue para o decodificador, que extrai opcode e campos de operando; o banco é lido em duas portas, uma para a entrada A da ALU e outra que passa por um MUX que escolhe entre o valor lido e um imediato sinal-estendido, alimentando B; o resultado da ALU bifurca-se entre um MUX antes da porta de escrita do banco e o endereço efetivo da memória de dados para loads e stores; em paralelo, um subcircuito calcula o próximo PC escolhendo entre incremento, alvo de desvio incondicional e alvo de desvio condicional segundo os flags. O PIC18 não é estritamente monociclo: usa as duas fases sobrepostas com prefetch, um passo intermediário entre monociclo e pipeline pleno, mas os componentes lógicos são essencialmente os mesmos.
Análise Quantitativa
A teoria materializa-se em uma fórmula simples. Sejam \iota_1, \ldots, \iota_m as instruções executadas e T(\iota_i) o número de ciclos de cada. O tempo de execução em segundos é o produto da soma de ciclos pelo tempo de um ciclo de máquina, T_{cm} = 4/f_{osc}. A 8 MHz, T_{cm} = 0{,}5\,\mu s. Como nem todas as instruções consomem o mesmo número de ciclos, definimos o CPI médio como média ponderada \overline{\text{CPI}} = \sum_k f_k \cdot \text{CPI}_k.
T_{exec} = T_{cm} \cdot \sum_{i=1}^{m} T(\iota_i) \qquad \text{IPS} = \frac{f_{osc}}{4 \cdot \overline{\text{CPI}}}
O CPI captura, em um número, quão bem o programa explora a arquitetura. No PIC18, código que evita desvios e chamadas em laços apertados tem CPI próximo de 1,0; dominado por desvios condicionais tomados, em torno de 1,3 a 1,5; com muitas chamadas a subrotinas curtas, ainda mais alto, porque o par chamada-retorno custa quatro ciclos. A 8 MHz e com \overline{\text{CPI}} = 1{,}2, o throughput é aproximadamente 1,67 milhões de instruções por segundo.
Ciclos típicos por classe de instrução no PIC18F4550
| Classe | Ciclos |
|---|---|
| Aritmética/lógica registrador-registrador | 1 |
| Acesso ao Access Bank | 1 |
Acesso a banco com MOVLB prévio |
1 + 1 prévio |
| Desvio condicional não tomado | 1 |
| Desvio condicional tomado | 2 |
GOTO, BRA |
2 |
CALL, RCALL, RETURN, RETLW |
2 |
Skip não disparado (BTFSS, BTFSC) |
1 |
| Skip disparado | 2 ou 3 |
Considere um laço com cem iterações, cada uma com cinco aritméticas monociclo e um desvio condicional ao topo: o total é 100 \cdot 5 + 99 \cdot 2 + 1 = 699 ciclos, ou cerca de 350 microssegundos a 8 MHz. Num sistema de tempo real com restrição de meio milissegundo, esse laço consome setenta por cento do orçamento — análise idêntica à exigida na segunda tarefa do Projeto Integrador.
Encerro com a Lei de Amdahl aplicada ao caminho de dados. Suponha aritméticas monociclo respondendo por oitenta por cento do tempo e desvios pelos vinte restantes. Dobrar a velocidade das aritméticas sem mexer nos desvios produz ganho global S = 1/(0{,}2 + 0{,}8/2) \approx 1{,}67, não 2,0. A fração não acelerada atua como teto inviolável; dobrar a velocidade de uma classe que responde por cinco por cento do tempo raramente compensa.
Antes de seguir ao Módulo 05, pergunte-se: quem produz, a cada ciclo, todos os sinais de seleção dos MUXes e habilitação de escrita que aqui apareceram apenas como entradas anônimas?
Síntese
Voltando à pergunta da abertura, as cinco operações conceituais cabem em quatro pulsos porque a busca da próxima instrução está sobreposta à execução da atual e porque decodificação, leitura de operando e computação são combinacionais. Os estados Q estruturam a coreografia: Q1 amostra o PC, Q2 carrega o IR e lê o operando, Q3 é o pico da ALU, Q4 escreve o destino e captura os flags. As três tarefas do Projeto Integrador amarram tudo: otimização de uso de WREG materializa o banco assimétrico e o Access Bank; a tabela experimental de ciclos exige metodologia rigorosa com Timer0, interrupções desabilitadas e estatística defensável; e a evidência das fases via I/O treina a inferência de comportamento interno por observação externa, habilidade central em sistemas embarcados. No Módulo 05, vamos atrás de quem produz os sinais de controle: a unidade de controle, modelada como máquina de estados finitos em duas vertentes, hardwired e microprogramada.