Comment la mémoire de pile est-elle utilisée pour les fonctions et les variables locales?

8

Je voulais enregistrer certaines valeurs dans l'EEPROM et je voulais également libérer de la mémoire SRAM en évitant certaines déclarations de variables, mais la mémoire EEPROM est en octets.

Si je veux stocker une valeur int, je dois utiliser plusieurs expressions à plusieurs reprises. Je pensais que je ferais quelques fonctions pour ceux-ci. Mais je crains que, si je crée une fonction, elle occupe toujours la mémoire SRAM, mieux je déclare une variable int au lieu d'utiliser EEPROM.

Comment les fonctions et les variables locales sont-elles stockées dans SRAM? Est-ce qu'il stocke uniquement l'adresse du pointeur fuction à partir de la mémoire flash ou toutes les variables et commandes sont stockées sur la pile?

Nafis
la source
4
N'oubliez pas que l'EEPROM n'est accessible en écriture que pour un nombre limité de fois, sa lecture est illimitée. Selon la fiche technique de l'AVR, l'EEPROM n'a que 100000 cycles, ce qui semble beaucoup, mais lorsque vous essayez de l'utiliser comme SRAM, cela ne durera qu'une période assez courte.
jippie
OMG! Après cela, l'EEPROM sera-t-elle inutile? Je vais vérifier la fiche technique!
Nafis
La mémoire Flash a également un style de vie. Il est plus sage de ne pas graver trop le programme.
Nafis
Avec une utilisation normale, les nombres donnés pour le flash et l'EEPROM ne posent aucun problème. L'équation change lorsque vous commencez à l'utiliser comme vous utilisez SRAM.
jippie

Réponses:

4

Seules les données de la fonction sont stockées sur la pile; son code reste en flash. Vous ne pouvez pas vraiment réduire l'utilisation de SRAM en utilisant plutôt l'EEPROM car, comme vous l'avez vu, l'EEPROM n'est pas adressable de la même manière. Le code pour lire et stocker l'EEPROM doit également utiliser de la mémoire SRAM - probablement autant de mémoire SRAM que vous tentiez d'enregistrer! L'EEPROM est également lente à écrire et a une durée de vie limitée (en nombre d'écritures dans chaque octet), ce qui la rend peu pratique à utiliser pour stocker le type de données temporaires que nous mettons habituellement sur la pile. Il est mieux adapté à la sauvegarde de données rarement modifiées, comme la configuration unique des appareils pour les appareils produits en série, ou à la capture d'erreurs peu fréquentes pour une analyse ultérieure.

Modifié: il n'y a pas de pile pour cette fonction jusqu'à ce que la fonction soit appelée, donc oui, c'est à ce moment-là que les données de la fonction y sont mises. Ce qui se passe après le retour de la fonction, c'est que sa trame de pile (sa zone réservée de SRAM) n'est plus réservée. Il sera éventuellement réutilisé par un autre appel de fonction. Voici un schéma d'une pile C en mémoire. Lorsqu'un cadre de pile n'est plus utile, il est simplement libéré et sa mémoire devient disponible pour être réutilisée.

JRobert
la source
Je pense de cette façon, lorsque la fonction est appelée, alors seulement les données à l'intérieur sont stockées dans la pile. Après l'exécution de la fonction, les données sont effacées de la pile / SRAM. Ai-je raison?
Nafis
5

Les variables locales et les paramètres de fonction sont stockés sur la pile. Cependant, ce n'est pas une raison pour ne pas les utiliser. Les ordinateurs sont conçus pour fonctionner de cette façon.

La mémoire de pile n'est utilisée que lorsqu'une fonction est active. Dès que la fonction revient, la mémoire est libérée. La mémoire de pile est une BONNE chose.

Vous ne voulez pas utiliser de fonctions récursives avec beaucoup de niveaux de récursivité ou allouer beaucoup de grandes structures sur la pile. Une utilisation normale est cependant très bien.

La pile 6502 ne fait que 256 octets, mais l'Apple II fonctionne très bien.

Duncan C
la source
Donc, vous voulez dire que la fonction sera enregistrée avec toutes ses variables locales, paramètres et expressions dans la pile temporairement, uniquement lorsqu'elle est appelée? Sinon, il restera dans le programme / la mémoire flash? Après exécution, sera-t-il effacé de la pile? Je parlais d'Arduino en fait, comme c'est Arduino Forum, je ne l'ai pas mentionné.
Nafis
Non, seuls les paramètres de la fonction et les variables locales sont sur la pile. Le code de la fonction n'est pas enregistré sur la pile. Ne pensez pas trop à cela.
Duncan C
5

L'AVR (la famille de microcontrôleurs traditionnellement utilisée sur les cartes Arduino) est une architecture Harvard , ce qui signifie que le code exécutable et les variables sont dans deux mémoires distinctes - dans ce cas, flash et SRAM. Le code exécutable ne quitte jamais la mémoire flash.

Lorsque vous appelez une fonction, l'adresse de retour est généralement envoyée à la pile - l'exception est lorsque l'appel de fonction se produit à la fin de la fonction appelante. Dans ce cas, l'adresse de retour de la fonction qui a appelé la fonction appelante sera utilisée à la place - elle est déjà sur la pile.
Si d'autres données sont placées sur la pile, cela dépend de la pression de registre dans la fonction appelante et dans la fonction appelée. Les registres sont la zone de travail du CPU, l'AVR a 32 registres de 1 octet. Les registres sont accessibles directement par des instructions CPU, tandis que les données dans SRAM devront d'abord être stockées dans des registres. Ce n'est que si les arguments ou la variable locale sont trop grands ou trop nombreux pour tenir dans les registres qu'ils seront placés sur la pile. Cependant, les structures sont toujours stockées sur la pile.

Vous pouvez lire les détails de l'utilisation de la pile par le compilateur GCC sur la plate-forme AVR ici: https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout
Lisez les sections "Frame Layout" et "Calling Convention" .

user2973
la source
1

Immédiatement lors d'un appel de fonction entrant dans la fonction, le premier code qui est exécuté consiste à décrémenter le pointeur de pile d'une quantité égale à l'espace requis pour les variables temporaires internes à la fonction. La chose brillante à ce sujet est que toutes les fonctions deviennent donc rentrantes et récursives, car leurs variables sont construites sur la pile du programme appelant. Cela signifie que si une interruption arrête l'exécution d'un programme et transfère l'exécution à un autre, elle peut également appeler la même fonction sans interférer les unes avec les autres.

Paul Dent
la source
1

J'ai essayé très fort de créer un exemple de code pour démontrer ce que les excellentes réponses ici disent, sans succès jusqu'à présent. La raison en est que le compilateur optimise agressivement les choses. Jusqu'à présent, mes tests n'ont pas du tout utilisé la pile, même avec des variables locales dans une fonction. Les raisons sont les suivantes:


  • Le compilateur peut en ligne l'appel de fonction, donc l'adresse de retour peut ne pas être poussée du tout sur la pile. Exemple:

    void foo (byte a) { digitalWrite (13, a); } void loop () { foo (5); }

    Le compilateur transforme cela en:

    void loop () { digitalWrite (13, 5); }

    Aucun appel de fonction, aucune pile utilisée.


  • Le compilateur peut passer des arguments dans les registres , ce qui lui évite de les pousser sur la pile. Exemple:

    digitalWrite (13, 1);

    Se compile en:

    158: 8d e0 ldi r24, 0x0D ; 13 15a: 61 e0 ldi r22, 0x01 ; 1 15c: 0e 94 05 01 call 0x20a ; 0x20a <digitalWrite>

    Les arguments sont placés dans des registres et donc aucune pile n'est utilisée (à part l'adresse de retour pour appeler digitalWrite).


  • Les variables locales peuvent bien être placées dans des registres, ce qui évite d'avoir à utiliser de la RAM. Cela économise non seulement de la RAM, mais est plus rapide.

  • Le compilateur optimise les variables que vous n'utilisez pas. Exemple:

    void foo (byte a) { unsigned long bar [100]; bar [1] = a; digitalWrite (9, bar [1]); } void loop () { foo (3); } // end of loop

    Maintenant, il faut allouer 400 octets pour "bar", n'est-ce pas? Nan:

    00000100 <_Z3fooh>: 100: 68 2f mov r22, r24 102: 89 e0 ldi r24, 0x09 ; 9 104: 0e 94 cd 00 call 0x19a ; 0x19a <digitalWrite> 108: 08 95 ret 0000010a <loop>: 10a: 83 e0 ldi r24, 0x03 ; 3 10c: 0e 94 80 00 call 0x100 ; 0x100 <_Z3fooh> 110: 08 95 ret

    Le compilateur a optimisé l' ensemble du tableau ! Cela peut dire que nous ne faisons vraiment qu'un digitalWrite (9, 3)et c'est ce qu'il génère.


Morale de l'histoire: n'essayez pas de dépasser le compilateur.

Nick Gammon
la source
La plupart des fonctions non triviales utilisent la pile pour sauvegarder certains registres, afin que ceux-ci puissent être utilisés pour contenir des variables locales. Ensuite, nous avons cette drôle de situation où le cadre de pile de la fonction contient des variables locales appartenant à son appelant .
Edgar Bonet du