Módulo 4: Exercícios — Unidade Central de Processamento: O Caminho de Dados

Estes três exercícios foram elaborados para que você aplique, de forma progressiva, os conceitos estudados no material do Módulo 4. Antes de começar, revise mentalmente os componentes do caminho de dados — ULA, banco de registradores, barramentos, multiplexadores e o ciclo de instrução — porque cada exercício exigirá que você raciocine sobre o que acontece fisicamente dentro do processador. Registre por escrito todos os seus passos, incluindo as justificativas para cada escolha: a precisão técnica na argumentação é tão importante quanto o resultado final.


Exercício 1 — Nível Básico

Anatomia da ULA e dos Flags de Status no PIC18F4550

Durante a primeira sessão de tutoria do Projeto Integrador, seu grupo está depurando um trecho do firmware que calcula a média de 4 leituras do conversor A/D e exibe o resultado no display LCD Sunstar 2004A. Ao executar o código passo a passo no simulador do MPLAB X, você percebe que o registrador STATUS assume valores inesperados após certas instruções aritméticas. Para entender o que está acontecendo, você decide estudar com cuidado como a ULA do PIC18F4550 funciona e como ela atualiza os flags de status.

Parte A — Operações da ULA e o Registrador STATUS

O material descreve que a ULA do PIC18F4550 produz, além do resultado da operação, um conjunto de flags de status no registrador STATUS. Considere a seguinte sequência de instruções em assembly executadas sobre o PIC18F4550:

MOVLW   0xC8        ; instrução 1
MOVWF   leitura_a, ACCESS  ; instrução 2
MOVLW   0x64        ; instrução 3
ADDWF   leitura_a, W, ACCESS  ; instrução 4
MOVWF   soma, ACCESS ; instrução 5
MOVLW   0x02        ; instrução 6
ANDWF   soma, W, ACCESS  ; instrução 7

Para cada instrução marcada abaixo, responda: (i) qual operação a ULA executa; (ii) qual é o valor resultante em hexadecimal; (iii) quais flags do registrador STATUS são afetados por essa instrução; e (iv) qual é o estado de cada flag afetado (0 ou 1) após a instrução executar, justificando com base nas equações e definições do material.

As instruções a analisar são a instrução 4 (ADDWF leitura_a, W, ACCESS) e a instrução 7 (ANDWF soma, W, ACCESS).

Em seguida, explique por que a instrução 2 (MOVWF leitura_a, ACCESS) não altera os flags Z, C, DC e OV, mas poderia alterar o flag N — e sob qual condição específica isso aconteceria.

Parte B — O Somador Ripple-Carry e o Flag Carry

O material apresenta o somador ripple-carry como o circuito fundamental de adição da ULA. Considere que o PIC18F4550 precisa somar os valores 0xFF e 0x01.

Trace, passo a passo, a propagação do carry através dos 8 somadores completos do ripple-carry para essa operação. Para isso, preencha a tabela a seguir indicando, para cada posição de bit, os valores de A[i], B[i], o carry de entrada Cin[i], o bit de soma S[i] e o carry de saída Cout[i]:

Bit (i) A[i] B[i] Cin[i] S[i] Cout[i]
0
1
2
3
4
5
6
7

Com base no preenchimento da tabela, qual é o resultado de 8 bits da soma? Qual é o valor do flag Carry (C) no registrador STATUS após essa operação? O que esse valor do flag C significa para um programador que utiliza variáveis do tipo uint8_t (sem sinal) no firmware do Projeto Integrador?

Parte C — Registrador W como Acumulador Central

O material afirma que a arquitetura do PIC18F4550 é de acumulador, com o registrador W funcionando como ponto de passagem obrigatório para a maioria das operações aritméticas e lógicas.

Considere que o firmware do Projeto Integrador precisa calcular a expressão resultado = (a AND 0x0F) + b, onde a e b são variáveis na RAM e resultado também deve ser armazenado na RAM. Escreva a sequência completa de instruções assembly do PIC18F4550 necessária para realizar esse cálculo. Para cada instrução, indique: (i) o que a instrução faz; (ii) qual é o estado do registrador W após a instrução; e (iii) se o conteúdo de W é sobrescrito e o que é perdido, se houver perda.

Ao final, responda: essa arquitetura de acumulador tem alguma desvantagem comparada a uma arquitetura com múltiplos registradores de propósito geral (como um processador ARM)? Justifique sua resposta com base em pelo menos um cenário concreto relacionado ao código do Projeto Integrador.

Parte D — SFRs e o Caminho de Dados

O material distingue os Registradores de Função Especial (SFRs) dos Registradores de Propósito Geral (GPRs), e explica que os SFRs estão no Banco 15 e são acessíveis via Access Bank.

No firmware do Projeto Integrador, seu grupo precisa configurar o PORTD como saída e escrever o valor 0xAA nos pinos. Escreva as instruções assembly necessárias para: (i) configurar todos os pinos do PORTD como saída usando o registrador TRISD; (ii) escrever 0xAA no registrador LATD. Para cada instrução, identifique se o endereço do registrador alvo pertence ao Access Bank ou requer configuração do BSR, e justifique com base na organização de memória do PIC18F4550 descrita no material.


Exercício 2 — Nível Intermediário

Ciclo de Instrução, Pipeline e Estimativa de Tempo de Execução

O firmware do Projeto Integrador precisa enviar um frame completo de 80 caracteres ao display LCD Sunstar 2004A com a menor latência possível, pois atualizações lentas do display causam cintilação visível durante as leituras do sensor ADC. Para isso, seu grupo decidiu estudar o custo real de execução de cada instrução, rastrear o comportamento do pipeline de dois estágios do PIC18F4550 e calcular o tempo total necessário para diferentes estratégias de implementação.

Parte A — Traçando o Ciclo de Instrução

Considere a seguinte sequência de instruções que faz parte da rotina de envio de dados ao display:

loop_display:
    MOVF    POSTINC0, W     ; instrução 1
    MOVWF   PORTD, ACCESS   ; instrução 2
    BCF     LATE, 0, ACCESS ; instrução 3
    BSF     LATE, 0, ACCESS ; instrução 4
    DECFSZ  contador, F, ACCESS ; instrução 5
    BRA     loop_display    ; instrução 6

Para as instruções 1, 2 e 6 (quando o desvio é tomado), descreva cada fase do ciclo de instrução (busca, decodificação, execução, acesso à memória e escrita de resultado), indicando: (i) quais componentes do caminho de dados estão ativos em cada fase; (ii) quais barramentos são utilizados e em que direção os dados fluem; e (iii) qual é o valor lido ou escrito em cada fase, assumindo que FSR0 aponta para o byte 0x41 (‘A’) e que contador contém 0x05.

Parte B — Pipeline de Dois Estágios e Penalidade de Desvio

O material explica que o PIC18F4550 implementa um pipeline de dois estágios que permite buscar a próxima instrução enquanto a atual está em execução. Porém, quando um desvio é tomado, a instrução que já estava sendo buscada em paralelo pode ser inválida e é descartada.

Usando a sequência do laço loop_display da Parte A, construa um diagrama de temporização (similar ao diagrama Gantt do material) mostrando as fases de busca e execução para as 5 primeiras iterações do laço, explicitando em quais momentos ocorre descarte de instrução. Assuma que o laço começa com contador = 5.

Em seguida, calcule quantos ciclos de máquina adicionais o pipeline desperdiça ao longo das 5 iterações do laço em comparação com um processador hipotético que não tivesse pipeline (e portanto não sofreria penalidade de desvio). O que isso implica para o desempenho do laço?

Parte C — Estimativa do Tempo de Execução

O material apresenta a fórmula do tempo de execução:

T_{exec} = N \times CPI \times T_{clock}

Considere que o PIC18F4550 do Kit ACEPIC PRO V8.2 está configurado com oscilador interno de 8 MHz. O clock de instrução efetivo é f_{osc}/4, portanto opera a 2 MHz, com período de instrução de 500 ns.

Calcule o tempo total de execução das seguintes operações do firmware do Projeto Integrador:

  1. A rotina loop_display da Parte A, enviando 80 bytes ao display. Cada iteração do laço executa as instruções 1 a 5 com seus respectivos custos em ciclos, mais a instrução 6 quando o desvio é tomado (79 vezes) e o custo da instrução 5 quando ela pula (1 vez ao final). Calcule N total, o CPI médio ponderado e o T_{exec} resultante.

  2. Uma rotina alternativa que substitui o laço por 80 instruções MOVF POSTINC0, W seguidas de 80 instruções MOVWF PORTD, ACCESS e 80 pares BCF/BSF LATE, 0 — sem desvios — ou seja, o laço “desenrolado” (loop unrolling). Calcule N total e T_{exec} para essa abordagem.

Ao comparar os dois resultados, qual estratégia é mais rápida? Qual é a contrapartida em termos de espaço de memória de programa (Flash)? Considerando que o PIC18F4550 tem apenas 32 KB de Flash e que o firmware do Projeto Integrador precisa de espaço para outras rotinas, qual estratégia você recomendaria e por quê?


Exercício 3 — Nível Desafiador

Caminho de Dados, Otimização e Projeto de Rotinas Eficientes

Este exercício integra todos os conceitos do Módulo 4 em um cenário de análise e projeto que você encontrará diretamente no Projeto Integrador: compreender como o caminho de dados do PIC18F4550 impõe restrições e oportunidades ao código que você escreve, e como usar esse conhecimento para projetar rotinas que aproveitam ao máximo os recursos disponíveis.

Parte A — Análise Comparativa: Access Bank versus Banco Genérico

O material afirma que variáveis no Access Bank são acessíveis sem configurar o BSR, enquanto variáveis em outros bancos exigem uma instrução MOVLB adicional para selecionar o banco antes do acesso. Esse overhead pode parecer pequeno, mas em laços executados centenas de vezes torna-se significativo.

Considere as duas versões de uma rotina que calcula a média de 8 leituras do ADC armazenadas em um buffer:

/* Versão A: buffer declarado sem atributo de posicionamento */
uint8_t buffer_A[8];
uint8_t media_A;

void calcula_media_A(void) {
    uint16_t soma = 0;
    uint8_t i;
    for (i = 0; i < 8; i++) {
        soma += buffer_A[i];
    }
    media_A = (uint8_t)(soma >> 3);
}

/* Versão B: buffer forçado no Access Bank */
static uint8_t __near buffer_B[8];
static uint8_t __near media_B;

void calcula_media_B(void) {
    uint16_t soma = 0;
    uint8_t i;
    for (i = 0; i < 8; i++) {
        soma += buffer_B[i];
    }
    media_B = (uint8_t)(soma >> 3);
}

Para cada versão, analise: (i) em qual região da RAM o compilador XC8 tende a alocar as variáveis, e qual o custo em ciclos de máquina para acessá-las dentro do laço; (ii) se o compilador precisa gerar uma instrução MOVLB antes de acessar as variáveis do laço, e com que frequência isso ocorre; (iii) o custo total em ciclos de máquina para 8 iterações completas de cada versão, contabilizando todos os acessos à memória e eventuais instruções de seleção de banco.

Ao final, calcule a aceleração (speedup) da Versão B em relação à Versão A usando a fórmula S = T_A / T_B e expresse o resultado como porcentagem de melhoria. Discuta se essa melhoria é relevante para o Projeto Integrador, considerando que a leitura do ADC e a atualização do display ocorrem repetidamente no laço principal do firmware.

Parte B — Deslocamentos como Multiplicação e Divisão Eficientes

O material menciona que operações de deslocamento realizam multiplicação e divisão por potências de 2 sem precisar de um circuito multiplicador dedicado. Isso é especialmente valioso no PIC18F4550, que não possui instrução de multiplicação inteira genérica em sua ISA.

O firmware do Projeto Integrador precisa implementar duas operações sobre o valor de temperatura lido do sensor:

Operação 1 — Conversão de escala: o sensor ADC retorna um valor de 8 bits no intervalo [0, 255] que representa temperaturas de 0 °C a 51 °C (resolução de 0,2 °C por bit). Para exibir no display LCD em décimos de grau, o valor precisa ser multiplicado por 2 (0,2 × 10 = 2).

Operação 2 — Cálculo de média de potência de 2: após acumular 8 leituras em uma variável de 16 bits (soma de 8 valores de 8 bits, máximo = 2040), a média deve ser calculada dividindo por 8.

Para cada operação: (i) escreva a sequência completa de instruções assembly do PIC18F4550 que implementa a operação usando apenas deslocamentos (instruções RLCF, RLNCF, RRCF ou RRNCF) e operações básicas da ULA — sem multiplicação nem divisão explícita; (ii) identifique qual flag do registrador STATUS é atualizado a cada deslocamento e o que esse flag indica; (iii) calcule o número total de ciclos de máquina necessários para cada sequência.

Em seguida, compare o custo em ciclos de cada sequência com uma implementação hipotética que usasse uma instrução MUL de 1 ciclo (como existe em arquiteturas ARM Cortex-M0+) e discuta as implicações dessa diferença para a escolha de microcontrolador em projetos embarcados de tempo real.

Parte C — Projetando o Caminho de Dados de uma Rotina Crítica

No Projeto Integrador, a rotina de atualização do display é chamada a cada 50 ms (20 vezes por segundo) e precisa: (1) ler 4 valores da RAM (temperatura, umidade, modo de operação e um contador de ciclos); (2) formatar cada valor como string ASCII de 3 dígitos; (3) escrever os 80 caracteres no buffer de display; e (4) enviar o buffer ao display via PORTD.

Para a etapa (2) de formatação — converter um valor binário de 8 bits sem sinal para 3 dígitos decimais ASCII — dois algoritmos são possíveis:

Algoritmo X — Divisão por subtrações sucessivas: subtrai-se 100 do valor enquanto o resultado for não negativo (contando quantas vezes), obtendo o dígito das centenas; repete-se o processo subtraindo 10 para as dezenas; o resto é o dígito das unidades.

Algoritmo Y — Deslocamento e máscara de nibble: o valor é convertido para BCD (usando o flag DC e correção BCD após adições) e os nibbles são extraídos com operações AND e deslocamento para obter os dígitos individuais.

Para cada algoritmo: (i) descreva, em termos de componentes do caminho de dados, quais unidades são ativas em cada etapa (ULA, barramentos, registradores específicos, flags); (ii) estime o número de ciclos de máquina necessários para converter um byte em 3 dígitos ASCII, indicando as instruções do PIC18F4550 utilizadas e seus respectivos custos; (iii) identifique se o flag DC (Digit Carry) do registrador STATUS tem papel no algoritmo e como ele é utilizado.

Com base na análise, responda: qual algoritmo resulta em menor número de ciclos de máquina para a conversão de um único byte? Qual é o impacto dessa escolha sobre o tempo total da rotina de atualização do display, considerando que 4 conversões são necessárias por frame? Expresse a diferença em microssegundos, assumindo clock de instrução de 2 MHz.

Parte D — Síntese: Caminho de Dados como Ferramenta de Projeto

O material afirma que “compreender o caminho de dados revela oportunidades de otimização que não são óbvias olhando apenas para o código C”.

Com base em tudo que você analisou nas partes anteriores deste exercício, escreva um relatório técnico de no mínimo dois parágrafos respondendo à seguinte questão: de que forma o conhecimento do caminho de dados — especificamente da organização do banco de registradores, dos custos de instrução da ULA e do comportamento do pipeline — muda a forma como você escreve código C para o PIC18F4550 no contexto do Projeto Integrador?

Seu relatório deve: (i) citar pelo menos dois exemplos concretos de decisões de código (escolhas de tipo de dado, posicionamento de variável, estrutura de laço ou escolha de operação matemática) que seriam tomadas de forma diferente com e sem o conhecimento do caminho de dados; (ii) avaliar se o compilador XC8, sem nenhuma intervenção do programador, seria capaz de descobrir automaticamente as mesmas otimizações; e (iii) identificar um cenário no Projeto Integrador onde uma otimização manual do código — motivada pelo entendimento do caminho de dados — seria imprescindível para atender a um requisito de tempo real.

Dica para o Exercício 1, Parte B: lembre-se de que o carry de entrada (Cin) do somador do bit 0 é sempre 0 em uma operação de adição. O carry se propaga como uma “onda” da posição menos significativa para a mais significativa — daí o nome ripple-carry. Preencha a tabela coluna por coluna, da esquerda para a direita, sem pular etapas.

Dica para o Exercício 2, Parte C: para calcular o CPI médio ponderado, some os ciclos de todas as instruções executadas (contando cada ocorrência individualmente) e divida pelo número total de instruções. Instruções de desvio tomado custam 2 ciclos; não tomado, 1 ciclo. A instrução DECFSZ quando pula custa 2 ciclos; quando não pula, 1 ciclo.

Dica para o Exercício 3, Parte B: para multiplicar por 2 usando deslocamento à esquerda no PIC18F4550, use RLNCF (rotate left without carry) sobre o registrador que contém o valor. Para dividir por 8 (deslocamento à direita 3 vezes), aplique RRNCF três vezes consecutivas — ou use RRCF se precisar considerar o carry. Lembre-se de que variáveis de 16 bits requerem operações separadas no byte alto e no byte baixo, com propagação do carry entre eles.

Entregue um arquivo TXT contendo as respostas

Somente 1 entrega por grupo!!!