Programação de sistemas embarcados: Estruturas em C e CMSIS

Em sistemas embarcados, as estruturas podem fornecer uma maneira elegante, intuitiva e eficiente de acessar registradores de hardware. Esta última propriedade das estruturas é a base do Cortex Microcontroller Software Interface Standard (CMSIS).

As estruturas (ou “structs” em C) permitem agrupar várias variáveis ​​relacionadas e tratá-las como uma única unidade. Eles fornecem um mecanismo para estender o sistema de tipos da linguagem C introduzindo tipos definidos pelo usuário. Em sistemas embarcados, as estruturas podem fornecer uma maneira elegante, intuitiva e eficiente de acessar registradores de hardware. Esta última propriedade das estruturas é a base do Cortex Microcontroller Software Interface Standard (CMSIS). [3]. Você pode descobrir tudo isso na vídeo-aula nº 12 do curso “Programação de Sistemas Embarcados Modernos”:

Lição 12 – Estruturas em C e Cortex Microcontroller Software Interface Standard (CMSIS)

Typedef ou sem typedef?

A maneira de declarar estruturas (assim como uniões e enumerações) em C é bastante particular porque o identificador que segue a palavra-chave ‘struct’ é um etiqueta (por exemplo, estrutura fooo {…} ;). Curiosamente, a tag (por exemplo, fooo) ocupa um namespace diferente de variáveis, funções e tipos. Tal tag não pode ser usada sozinha, mas sempre deve ser precedida pela palavra-chave ‘struct’ para formar um especificador de tipo elaborado. Essas regras de ocultação de nomes de tags são provavelmente um erro no design da linguagem C, e eu recomendo seguir as diretrizes de Dan Saks para transformar tags em typedefs [1]. Para structs simples (não autorreferenciais), a declaração não precisa usar nenhuma marcação:

typedef struct {
   ...
} Foo;

Para estruturas autorreferenciais (como listas vinculadas ou árvores), a tag é necessária, mas o nome da tag é intencionalmente mantido igual ao nome typedef.

typedef struct List List;
struct List {
   List *next;
   ...
};

As duas declarações de estrutura recomendadas cumprem as diretrizes MISRA-C:2012, em particular a diretriz 2.4 [2].

Layout da estrutura na memória

O compilador C sempre respeitará o ordem struct membros exatamente como você os declarou em sua struct. Além disso, o primeiro membro da estrutura está sempre alinhado com o início da estrutura na memória. (Isto será útil para emular a herança em C, que você verá em capítulos futuros.)

Mas, conforme mostrado no vídeo, o compilador pode inserir bytes de preenchimento extras após cada membro, incluindo o último membro. O compilador adiciona preenchimento para obter o alinhamento ideal dos membros da estrutura para um acesso mais eficiente. Portanto, o número de bytes de preenchimento inseridos depende do processador e de suas regras de alinhamento específicas, o que significa que o layout da estrutura geralmente é não portátil.

Os compiladores integrados geralmente fornecem extensões de linguagem C não padrão para permitir que você evite o preenchimento em uma estrutura. Por exemplo, o compilador IAR no vídeo fornece a palavra-chave estendida ‘__packed’ que você pode aplicar a uma estrutura. No entanto, estruturas “comprimidas” podem exigir mais instruções de CPU para acessar membros desalinhados, conforme mostrado no vídeo após a mudança para o processador Cortex-M0.

Uma boa regra para minimizar o desperdício de memória por preenchimento é declarar os membros da estrutura começando do maior para o menor.

Estruturas em CMSIS

Uma estrutura em C define um layout de memória específico, que pode ser mapeado para blocos de registro de hardware associados. Isso permite que você acesse facilmente os registradores de bloco de hardware como uma estrutura membros, deixando todos os cálculos de endereço (deslocamentos desde o início da estrutura) para o compilador. Além disso, essa forma de acessar o hardware pode aproveitar os modos de endereçamento especiais dos processadores modernos (baseados em registradores com deslocamento imediato), que podem ser mais eficientes do que codificar endereços de hardware individuais.

Essa ideia é a base do Cortex Microcontroller Software Interface Standard (CMSIS) [3]que define as interfaces de hardware como estruturas C mapeadas para diferentes blocos de hardware.

Notas finais

“Todos os problemas de computação podem ser resolvidos por outro nível de indireção” [4]. Mas muitos níveis de indireção rapidamente se tornam contraproducentes. Com isso em mente, o CMSIS atinge o equilíbrio certo, fornecendo o nível certo de abstração para que os nomes dos membros da estrutura possam corresponder diretamente aos registros conforme aparecem nas folhas de dados.

[1] E Saks, “Tag vs. Type Names”, Embedded Systems Programming, Outubro de 2002

[2] Padrões MISRA-C:

[3] software ARM, Padrão de interface de software do microcontrolador Cortex (CMSIS)

[4] aforismo atribuído a David Wheeler Onde Butler Lampson


Conteúdo Relacionado:

Para mais embutidos, assine a newsletter semanal da Embedded.