🛠️ Aula 01 — Arquitetura de Sistemas Embarcados e Cadeia de Compilação Cruzada
Navegação Rápida
📍 Disciplina: 03 Programação Aplicada a Sistemas Elétricos | 🗺️ Área: 06 Computação e Telecomunicações | 🏠 Hub Principal ⬅️ Disciplina Anterior: 02 Pensamento Computacional | ➡️ Próxima Aula: Aula 02 - Linguagem C e Bitwise
📌 Sumário da Aula
- 🧠 1. Sistemas Embarcados na Engenharia Elétrica Moderna
- ⚙️ 2. Arquiteturas de Computadores: von Neumann vs. Harvard e CISC vs. RISC
- 🔄 3. O Paradoxo da Compilação Cruzada (Cross-Compilation)
- 🔨 4. As Etapas Formais da Toolchain GNU (CPP, GCC, AS, LD)
- 🗺️ 5. Anatomia de Memória, Seções ELF e o Linker Script
- 📊 6. Diagrama da Toolchain de Compilação Cruzada
- 📝 7. Questões de Concursos e Problemas Avançados Resolvidos
- 🔗 8. Conexões e Referências Cruzadas
🧠 1. Sistemas Embarcados na Engenharia Elétrica Moderna
Um Sistema Embarcado (Embedded System) é um sistema computacional microprocessado ou microcontrolado projetado para desempenhar funções dedicadas, específicas e em tempo real dentro de um sistema elétrico, mecânico ou industrial maior.
Diferente de um computador pessoal de uso geral (desktop/servidor), que executa múltiplos aplicativos arbitrários sob demanda do usuário, um sistema embarcado na Engenharia Elétrica opera continuamente com alta confiabilidade, restrições rígidas de consumo energético, espaço físico e determinismo temporal absoluto.
Exemplos canônicos de sistemas embarcados no setor elétrico:
- Relés Numéricos de Proteção (IEDs - Intelligent Electronic Devices): Executam algoritmos de proteção diferencial de transformadores (função ANSI 87T), sobrecorrente temporizada (51) e distância (21), com janelas de decisão inferiores a ;
- Inversores Solares Fotovoltaicos e Conversores Eólicos: Processam algoritmos de rastreamento do ponto de máxima potência (MPPT) e modulação por largura de pulso vetorial (SVPWM) com frequências de amostragem de a ;
- Medidores Eletrônicos Inteligentes (Smart Meters / AMI): Calculam consumo de energia ativa e reativa em 4 quadrantes, distorção harmônica total (THD) e comunicam-se via protocolo celular ou PLC (Power Line Communication);
- Controladores Lógicos Programáveis (CLPs): Automatizam processos de subestações e acionamentos de motores industriais através de barramentos determinísticos.
⚙️ 2. Arquiteturas de Computadores: von Neumann vs. Harvard e CISC vs. RISC
Os microcontroladores industriais modernos (como a família ARM Cortex-M, Texas Instruments C2000 DSP e Microchip AVR/PIC) fundamentam-se em duas classificações arquiteturais essenciais:
flowchart TD subgraph VN["Arquitetura von Neumann"] CPU1["CPU"] <--> BarramentoUnico["Barramento Compartilhado Único<br/>(Dados + Instruções)"] BarramentoUnico <--> MemoriaUnica["Memória Unificada"] end subgraph HV["Arquitetura Harvard"] CPU2["CPU"] <--> BarrInst["Barramento de Instruções"] <--> Flash["Memória de Programa (Flash)"] CPU2 <--> BarrDados["Barramento de Dados"] <--> SRAM["Memória de Dados (RAM)"] end
2.1 Von Neumann vs. Harvard
- Arquitetura von Neumann: Possui um único espaço de memória e um único conjunto de barramentos compartilhados para instruções de programa e dados. Apresenta o clássico Gargalo de von Neumann (von Neumann Bottleneck): a CPU não pode ler uma instrução e acessar um operando em memória simultaneamente no mesmo ciclo de clock.
- Arquitetura Harvard (e Harvard Modificada): Possui memórias fisicamente separadas para instruções (geralmente Flash ROM não volátil) e dados (SRAM volátil), interligadas por barramentos independentes de endereços e dados. A CPU busca a próxima instrução de código (fetch) no barramento de programa no exato momento em que lê ou grava dados numéricos (read/write) no barramento de dados, permitindo a implementação de Pipelines profundos com vazão de 1 instrução por ciclo (). A arquitetura ARM Cortex-M utiliza a variante Harvard Modificada (barramentos I-Code e D-Code conectados a um barramento de sistema compartilhado).
2.2 RISC vs. CISC
- RISC (Reduced Instruction Set Computer): Conjunto reduzido de instruções ortogonais de tamanho fixo (ex: 32 bits em ARM ou 16 bits em Thumb), arquitetura load-store estrita (operações lógicas e aritméticas só ocorrem entre registradores internos da CPU, nunca diretamente na memória).
- CISC (Complex Instruction Set Computer): Conjunto abrangente de instruções de tamanho variável com múltiplos modos de endereçamento complexos capazes de operar diretamente sobre operandos na memória (ex: x86).
🔄 3. O Paradoxo da Compilação Cruzada (Cross-Compilation)
Na programação convencional de desktop, a compilação é nativa: o código-fonte C é compilado em uma máquina x86_64 para gerar um arquivo executável que rodará na própria CPU x86_64.
Em sistemas embarcados, surge o desafio da Compilação Cruzada (Cross-Compilation):
- O microcontrolador de destino (Target) possui recursos severamente restritos (ex: processador ARM Cortex-M4 operando a , com apenas de Flash e de RAM), incapaz de hospedar um compilador complexo, editor de código e analisador sintático;
- Utiliza-se um computador hospedeiro (Host), tipicamente uma estação de trabalho x86_64 multicore com gigabytes de memória RAM, executando Linux;
- O compilador roda na arquitetura do Host, mas gera instruções de código de máquina binárias especificamente codificadas para o conjunto de instruções (ISA) do processador Target.
flowchart LR Host["Máquina Host (x86_64)<br/>Linux / Ubuntu<br/>arm-none-eabi-gcc"] -->|Gera Binário ELF/HEX| Target["Dispositivo Target (ARM Cortex-M)<br/>Sem Sistema Operacional (Bare-Metal)<br/>ou RTOS (FreeRTOS)"] Host -->|Depuração via OpenOCD / SWD| Target
A nomenclatura padronizada das ferramentas GNU para compilação cruzada segue o formato triplo ou quádruplo:
Exemplo: arm-none-eabi-gcc:
arm: Arquitetura do processador de destino (ARM 32 bits);none: Sem fornecedor específico (genérico);eabi: Embedded Application Binary Interface (execução em metal puro / bare-metal sem sistema operacional Linux);gcc: GNU Compiler Collection.
🔨 4. As Etapas Formais da Toolchain GNU (CPP, GCC, AS, LD)
A transformação de arquivos-fonte C (.c e .h) no binário executável final desdobra-se em quatro fases rigorosamente encadeadas:
flowchart TD Src["Código Fonte C (.c / .h)"] --> CPP["1. Pré-processador (cpp)<br/>arm-none-eabi-cpp"] CPP -->|Fonte Expandido (.i)| CC1["2. Compilador C (cc1)<br/>arm-none-eabi-gcc -O2"] CC1 -->|Código Assembly (.s)| AS["3. Montador (as)<br/>arm-none-eabi-as"] AS -->|Código Objeto (.o ELF Relocável)| LD["4. Linker / Ligador (ld)<br/>arm-none-eabi-ld -T script.ld"] Libs["Bibliotecas (libc, libm)"] --> LD LinkerScript["Linker Script (.ld)"] --> LD LD --> ELF["Executável ELF Final (.elf)"] ELF --> Objcopy["Utilitário Objcopy"] Objcopy --> HEX["Arquivo Intel HEX (.hex) ou Raw BIN (.bin)"]
4.1 Fase 1: Pré-processamento (arm-none-eabi-cpp)
O pré-processador executa diretivas textuais iniciadas por #:
- Expansão de arquivos de cabeçalho:
#include <stdint.h>copia o conteúdo integral do arquivo para o código; - Substituição de macros literais:
#define FREQ_CORTE 60.0substitui todas as ocorrências no texto; - Compilação condicional:
#ifdef DEBUG ... #endifinclui ou remove blocos inteiros de código antes da análise sintática; - Remoção de comentários.
Saída: Arquivo C puro expandido com extensão
.i.
4.2 Fase 2: Compilação C (arm-none-eabi-gcc)
O compilador realiza a análise léxica, sintática e semântica do código expandido, constrói a Árvore Sintática Abstrata (AST) e gera as rotinas equivalentes na linguagem assembly do alvo. É nessa fase que atuam os níveis de otimização:
-O0: Sem otimização (facilita depuração passo a passo com GDB);-O2: Otimização agressiva para velocidade sem inflar o tamanho do código;-Os: Otimização máxima voltada para a redução do tamanho do código binário (size), essencial para microcontroladores com Flash restrita. Saída: Arquivo Assembly humano-legível com extensão.s.
4.3 Fase 3: Montagem (Assembly - arm-none-eabi-as)
O montador traduz os mnemônicos textuais do assembly (ex: LDR r0, [r1, #4], ADDS r2, r0, r1) para os códigos de operação (opcodes) hexadecimais binários específicos da CPU.
Saída: Arquivo de Código Objeto Relocável com extensão .o no formato padronizado ELF (Executable and Linkable Format). Este arquivo contém tabelas de símbolos, mas os endereços absolutos de memória física ainda não estão resolvidos.
4.4 Fase 4: Vinculação / Ligação (Linking - arm-none-eabi-ld)
O linker junta todos os arquivos .o do projeto, resolve referências externas entre funções e variáveis globais, incorpora o código de funções chamadas das bibliotecas de sistema (como rotinas trigonométricas de libm.a) e aloca cada byte nos endereços físicos reais especificados pelo Linker Script.
Saída: Arquivo ELF executável final com extensão .elf.
🗺️ 5. Anatomia de Memória, Seções ELF e o Linker Script
Em um sistema embarcado bare-metal, o mapa de memória física é fixo pelo fabricante do chip. O compilador particiona o código e os dados em Seções Lógicas Canônicas:
| Seção ELF | Conteúdo Armazenado | Memória Física | Persistência / Acesso |
|---|---|---|---|
.isr_vector | Tabela com o ponteiro inicial de Stack e os endereços das rotinas de interrupção | Flash ROM (início em 0x08000000) | Não volátil / Somente leitura |
.text | Instruções binárias executáveis do programa compilado | Flash ROM | Não volátil / Somente leitura |
.rodata | Constantes literais (const float PI = 3.14159f;, lookup tables de senoide) | Flash ROM | Não volátil / Somente leitura |
.data | Variáveis globais e estáticas inicializadas com valores não nulos (int status = 1;) | SRAM (VMA: 0x20000000) / Copiada da Flash (LMA) | Volátil / Leitura e Escrita |
.bss | Variáveis globais e estáticas não inicializadas ou inicializadas com zero (int buffer[100];) | SRAM | Volátil / Zerada pelo startup |
Heap | Memória dinâmica alocada em tempo de execução via malloc() e free() | SRAM (cresce para cima) | Evitada em sistemas críticos |
Stack | Variáveis locais de funções, registros de ativação e contexto salvo de interrupções | SRAM (cresce para baixo a partir do topo) | Volátil / LIFO ultrarrápido |
5.1 O Linker Script (.ld)
O Linker Script é o arquivo que define os blocos físicos de memória e atribui cada seção lógica aos endereços de barramento:
/* Exemplo de Linker Script para STM32F4 (ARM Cortex-M4) */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.isr_vector : { *(.isr_vector) } > FLASH
.text : { *(.text) } > FLASH
.rodata : { *(.rodata) } > FLASH
/* .data possui LMA (Load Memory Address) na Flash e VMA (Virtual) na RAM */
.data : AT(LOADADDR(.rodata) + SIZEOF(.rodata))
{
_sdata = .;
*(.data)
_edata = .;
} > RAM
.bss :
{
_sbss = .;
*(.bss)
_ebss = .;
} > RAM
}O código de inicialização do microcontrolador (Startup Code, executado antes da função main()) utiliza os símbolos _sdata, _edata, _sbss e _ebss para:
- Copiar os valores iniciais da seção
.datada Flash para a RAM; - Preencher toda a área da seção
.bssna RAM com zeros absolutos (0x00).
📊 6. Diagrama da Toolchain de Compilação Cruzada
O diagrama técnico abaixo exibe o fluxo completo da toolchain de compilação cruzada partindo da máquina Host x86_64 até o particionamento de memória física no target ARM:
📝 7. Questões de Concursos e Problemas Avançados Resolvidos
Questão 1 (Banca CESPE/CEBRASPE - Perito Criminal Federal / Engenharia Eletrônica)
No desenvolvimento de software embarcado para um módulo registrador de oscilografia de perturbações em subestações elétricas baseado em microcontrolador ARM de 32 bits, um engenheiro observa que as variáveis globais alocadas em seu código C consomem diferentes segmentos de memória. Considere as seguintes declarações de variáveis no escopo global:
const float tensao_base_kv = 230.0f;uint32_t contador_eventos = 100U;uint16_t buffer_amostras_adc[2048];
Assinale a opção que indica, correta e respectivamente, em quais seções lógicas da imagem binária compilada (formato ELF) essas variáveis serão alocadas pelo linker script.
Alternativas:
A) .data, .bss e .rodata.
B) .rodata, .data e .bss.
C) .text, .rodata e .data.
D) .rodata, .bss e .data.
E) .bss, .data e .text.
Gabarito Comentado:
- Alternativa correta: B.
- Justificativa Detalhada:
tensao_base_kv: Declarada com o qualificadorconst, seu valor é de somente leitura e não pode ser alterado em tempo de execução. O compilador a aloca na seção.rodata(read-only data), que reside permanentemente na memória Flash ROM, poupando a escassa memória SRAM.contador_eventos: É uma variável global modificável inicializada com um valor explícito não nulo (100U). Ela é alocada na seção.data. Seus valores iniciais residem na Flash e são copiados para a SRAM durante a rotina de inicialização (startup).buffer_amostras_adc: É um vetor global declarado sem inicializador explícito. Por definição do padrão da linguagem C, variáveis estáticas e globais não inicializadas são alocadas na seção.bss(Block Started by Symbol) e zeradas na memória SRAM antes da invocação da funçãomain().
Questão 2 (Banca FCC - Analista Judiciário / Tecnologia da Informação e Automação)
A compilação de um sistema embarcado com o compilador GCC utilizando a flag de otimização -Os tem como objetivo principal:
A) Maximizar o paralelismo de execução em múltiplos núcleos de CPU.
B) Desativar completamente a geração de interrupções de hardware pelo NVIC.
C) Otimizar o código gerado visando primariamente à redução do tamanho do arquivo binário final (size).
D) Substituir todas as chamadas de funções recursivas por laços de repetição infinitos.
E) Converter variáveis de ponto flutuante de precisão dupla em números inteiros de 8 bits.
Gabarito Comentado:
- Alternativa correta: C.
- Justificativa: No GNU GCC, as flags de otimização representam compromissos de projeto:
-O0(sem otimização, depuração),-O1(otimização básica),-O2(otimização balanceada com foco em velocidade),-O3(otimização agressiva de velocidade, podendo aumentar o tamanho do código através de desenrolamento de laços / loop unrolling e expansão de funções inline). A flag-Os(Optimize for Size) ativa todas as otimizações de-O2que não aumentem o tamanho do código, aplicando transformações específicas para minimizar a ocupação em bytes na memória Flash ROM do microcontrolador.
Questão 3 (Problema Prático de Engenharia - Dimensionamento de Memória por Linker Script)
Um firmware de telemetria para religadores automáticos desenvolvido para um microcontrolador com de memória Flash e de memória SRAM apresentou o seguinte relatório de saída gerado pelo comando arm-none-eabi-size:
text data bss dec hex filename
412800 8192 48500 469492 729f4 firmware.elfDetermine:
- A ocupação total da memória Flash ROM em bytes e a respectiva porcentagem utilizada da capacidade do chip.
- A ocupação total da memória volátil SRAM em bytes e a respectiva porcentagem utilizada.
- Avalie se o programa pode ser gravado e executado com segurança no microcontrolador especificado.
Resolução Passo a Passo:
-
Cálculo da Ocupação da Memória Flash ROM (): A memória Flash armazena o código executável (
.text+.rodata) e os valores iniciais de carga da seção.data(LMA):Capacidade total da Flash:
Porcentagem de utilização da Flash:
-
Cálculo da Ocupação da Memória SRAM (): A memória SRAM aloca as variáveis modificáveis inicializadas (
.data) e as não inicializadas (.bss):Capacidade total da SRAM:
Porcentagem de utilização estática da RAM:
-
Avaliação Crítica de Segurança Operacional:
- A memória Flash possui margem de folga de ( livres), o que é suficiente para futuras atualizações de firmware.
- A memória SRAM possui apenas ().
Veredito de Engenharia: Alerta Crítico de Risco Operacional. Embora o código caiba estaticamente no microcontrolador, o espaço residual de deve acomodar simultaneamente a Stack (pilha de execução das chamadas de função e empilhamento automático de interrupções de hardware pelo NVIC). Se uma rotina de falta disparar múltiplas interrupções aninhadas ou funções com buffers locais grandes, ocorrerá um Stack Overflow, corrompendo a seção
.bsse travando o religador em campo. Recomenda-se reduzir o tamanho de buffers estáticos ou migrar para um modelo de microcontrolador com pelo menos de SRAM.
🔗 8. Conexões e Referências Cruzadas
Disciplinas Vinculadas:
- 02 Pensamento Computacional: Níveis de abstração e arquitetura de hardware.
- Aula 02: Operações bitwise e manipulação de tipos exatos em registradores.
- 05 Microprocessadores: Arquitetura interna de barramentos e temporizadores de hardware.
- 09 Sistemas de Proteção e Medição: Aplicação em relés IEDs e registradores de perturbação.
Materiais Didáticos Oficiais da Disciplina:
- Arquivo de Curso:
01 - Bases Para Programação de Sistemas Embarcados.pdf - Arquivo de Curso:
04 - Linguagem C Nos Sitemas Embarcados.pdf - Arquivo de Apoio:
00 - Plano de Ensino.pdf