Segments de mémoire sur microcontroleur AVR

 Sans catégorie  Commentaires fermés sur Segments de mémoire sur microcontroleur AVR
Oct 072015
 

Je précise que ce qui suit est principalement valable pour les programmes compilés avec GCC pour microcontroleur AVR 8Bit, plus particulièrement le Atmega328

Un programme se découpe en différents segments de mémoire qui sont principalement

  • .text : Le programme lui-même
    • .text.startup : Le « startup-code »
    • .text.exit : Le « exit-code » theoriquement jamais appelé
  • .data : Les données statiques initialisées
  • .bss : Les données statiques non-initialisées

ainsi que de segments spécifiques qui sont

  • .rodata : Les données statiques constantes en ram
  • .progmem.data : Les données statiques constantes en rom
  • .user_heap_stack : Les données allouées dynamiquement ( malloc() )

.text contient le code du programme, les opcodes, les instructions. et finira gravée dans la flash du microcontroleur.
par exemple

void foo()
{   //do something
}

Une confusion courante viens du fait que « .text » est souvent assimilé à « flash » ce qui n’est pas complètement faux mais très réducteur.
Un affichage au format Berkley montrera dans la colonne .text une fusion de .progmem.data
Le format d’affichage le plus fiable est le format SysV

.bss Est le segment qui contient les variables globales ou statiques non initialisées.
par exemple

int32_t myGlobal;

ou

void foo(){
static int32_t myLocalStaticVar;
}

Il faut voir .bss comme le grand tableau d’allocation statique des variables.
A noter que généralement le segment .bss est rempli de zero par le startupcode mais ce n’est pas garanti que ce soit toujours le cas.

.data contient les données statiques INITIALISEES du programme,
Par exemple

int32_t myVar = 0x12345678;

ou

void foo(){
static int32_t myLocalStaticVar = 0x12345678;
}

Très similaire à .bss mais pour les variables initialisées.
Ces variables globales ou statiques sont situées dans le segment .data (leur adresse statique) et la valeur d’initialisation 0x12345678; est une constante située dans la flash. Cette valeur d’initialisation sera copiée en RAM dans le startup code.
En bref cette variable prend 2 fois de la place : une fois en ram pour être utilisée et une fois en rom pour la valeur d’initialisation.
A noter que la valeur d’initialisation n’est généralement pas comptée dans .text (encore une fois ça dépends de l’outil) et que pour calculer la quantité de flash réellement utilisée il faut ajouter .text et .data

.bss et .data sont sensiblement identiques dans leur usage. Les deux contiennent des données globales statiques (peu importe leur visibilité globale ou locale).
Cependant elles sont séparées pour que le startup code puisse faire toutes les initialisations en une seule fois.
(en copiant tout le segment .data d’un coup et en couvrant de 0x00 tout le segment .bss d’un coup).

.rodata est une spécificité liée à l’architecture harvard des cpu AVR qui concerne les données constantes.
Par exemple

const char greeting[] = "Bonjour, Comment allez vous ?"

Normalement, cette chaine de caractères serait dans l’espace programme : la section .text
Sauf que .text est en rom et que les CPU AVR ne peuvent pas lire leur ROM de la même façon qu’ils lisent leur RAM. (Ce n’est pas un grand espace d’adresse unifié)
De ce fait, les données constantes doivent être copiées en ram de la même façon que les variables initialisées (une constante a forcément une valeur initiale) dans la section .data.
Sauf que comme ces données sont constantes on leur fait une section à elles nommée .rodata.

Cette section constitue un gaspillage de ram considérable sur des valeurs potentiellement volumineuses.
C’est pourquoi on essaie de stocker les valeurs constantes en ROM quitte a avoir une procédure d’accès un peu complexe et plus lourde, d’où la section suivante.

.progmem.data est une zone de flash que certains outils considèrent un sous section de .text, ce qui peut donner lieu à des confusions. Encore une fois, utiliser le format d’affichage sysV

avr-size -A fichier.o [...]

C’est moins beau que l’affichage en colonne Berkley mais c’est plus fiable.
Les données sont déclarées de la façon suivante

const char greeting[] __attribute__ ((progmem)) = "Bonjour, Comment allez vous ?"

ou plus simplement

const char greeting[] PROGMEM = "Bonjour, Comment allez vous ?"

De cette façon la constante sera stockée en flash sans aller dans l’infâme section .rodata
Attention : le (const char *) résultant est du même type que si la constante était dans .rodata mais on ne peut pas la lire directement

greeting[0] != 'B' // ou alors par hasard

la lecture se fait à l’aide d’une fonction de la librairie standard avr

pgm_read_byte(greeting)

Les opérations sur le pointeur telles que incrémentation/décrémentation restent valides.
on pourra par exemple faire

pgm_read_byte( &(greeting[10]) )

ou

pgm_read_byte( greeting+10 )

ça tient toujours compte automatiquement compte de sizeof()
par exemple

const uint16_t table[] PROGMEM = { 0x1234 , 0xBABE, 0xFACE, 0xDEAD, 0xBEEF, 0x5678 };

pgm_read_word( table + 2 ); //renvoie bien 0xFACE et non pas 0xBABE
pgm_read_word( &(table[2]) ); //pareil