Question:
Compresser le fichier .hex pour le micro-contrôleur
Danial
2019-10-12 15:53:18 UTC
view on stackexchange narkive permalink

J'écris actuellement un programme dans AVR Studio , voici le build * Utilisation de la mémoire: *

  Périphérique: atmega32
    Programme: 9304 octets (28,4% plein)
    (.text + .data + .bootloader)
    Données: 334 octets (16,3% pleins)
    (.data + .bss + .noinit)
 

Comme j'utilise un micro-contrôleur ATmega32 , cela ne semble pas être un problème, mais je souhaite utiliser ce même code et l'utiliser pour un micro ATmega8 -contrôleur.

Je souhaite donc réduire la taille du programme à moins de 8192 bytes.

Comment puis-je faire cela?

Utilisez-vous la bibliothèque à virgule flottante?
@Danial cela ne résout pas votre problème, mais je noterai simplement que le fichier .hex est juste un format dans lequel stocker le code du programme. Ce n'est que du texte, donc ils se compresseraient bien.Mais ce n'est pas ce qui se termine dans le flash du MCU, c'est le code de programme réel.Donc _c'est_ ce que vous voulez réduire, pas le fichier .hex en tant que tel.
Sept réponses:
Oldfart
2019-10-12 15:59:56 UTC
view on stackexchange narkive permalink

Vous pouvez pas compresser le code hexadécimal, vous pouvez seulement essayer de le réduire.

  • Essayez-le différemment?paramètres du compilateur (optimisation maximale et optimisation de la taille)
  • Choisissez votre chemin dans le code source et voyez ce qui peut être optimisé ou omis.
  • Vérifiez si du code de bibliothèque inutile est extrait. (Cela ne devrait pas l'être, mais qui sait)

Bon point de Jeroen3: Vérifiez si vous avez besoin / avez une virgule flottante.Surtout des fonctions comme printf tirent parfois le code en virgule flottante pour pouvoir gérer printf ("% f" ... .pas besoin de ça.

Michel Keijzers
2019-10-12 16:04:03 UTC
view on stackexchange narkive permalink

Le MCU ne peut pas exécuter de code compressé.

Cependant, vous pouvez faire certaines choses:

  • Au lieu d'utiliser des fonctions de bibliothèque à part entière, créez vous-même certaines ou toutes les fonctions;de cette façon, vous pouvez optimiser les fonctions de la bibliothèque qui sont pour la plupart (trop) flexibles pour vos besoins spécifiques.
  • Supprimez le code dupliqué dans votre propre code.Utilisez des paramètres pour le code presque dupliqué pour le rendre flexible.
  • Utilisez le plus petit type pour les constantes, et en particulier les tableaux de constantes.
  • Supprimez le code mort "évident" (code qui ne peut jamais être exécuté), voir la remarque de Jeroen3 et Dakkaron ci-dessous.(vérifiez si cela se produit pour des fonctions complètes ou une partie de fonctions).Voir également ce lien.
  • Diminuez les chaînes (si vous utilisez beaucoup d'instructions d'impression, réduisez ces chaînes constantes si possible).
La suppression du code mort est une [fonctionnalité de l'éditeur de liens] (https://renenyffenegger.ch/notes/development/languages/CC-plus-plus/GCC/options/Wl/index): ** - Wl, --gc-sections ** qui s'applique également aux fonctions de bibliothèque inutilisées.
@Jeroen3 Merci, je ne savais pas que c'était si intelligent.J'ai mis à jour ma réponse (avec un commentaire à votre nom).
@Jeroen3 Cela ne fonctionne que pour le code mort "dur", donc le code où le compilateur peut déduire qu'il est mort.Cela ne fonctionne pas pour le code mort que le compilateur ne peut pas déterminer, par exempledu code qui n'est exécuté que si la fonction est appelée avec un paramètre spécifique, mais la fonction n'est jamais appelée avec ce paramètre.Il est donc certainement utile d'utiliser la vérification de l'éditeur de liens, mais il est également utile d'analyser le code à la main pour le code mort.
@Dakkaron Merci ... J'ai ajouté votre nom au commentaire (en fait, j'avais quelque chose comme ce que vous avez écrit avant).
Michael Karas
2019-10-12 19:41:12 UTC
view on stackexchange narkive permalink

En plus des excellentes suggestions fournies dans les autres réponses ici, je tiens à dire qu'il peut y avoir une énorme différence dans la façon dont les compilateurs (et les éditeurs de liens) peuvent optimiser le code.

J'ai travaillé dans une entreprise il y a quelques années où le produit utilisait l'ATMega8. Lorsque je suis arrivé sur les lieux, ce produit avait trois versions de code source différentes pour fournir des ensembles distincts de fonctionnalités pour diverses configurations de produit. Le code source a été compilé à l'aide d'un compilateur C à faible coût et chaque jeu de codes tient à peine dans les 8K octets de la mémoire FLASH de l'appareil.

J'ai demandé à la société d'acheter un compilateur haut de gamme auprès d'une société bien connue dans la communauté AVR. Je suis ensuite allé travailler sur les codes sources du logiciel et définir les options du compilateur pour une optimisation maximale.

Lorsque j'ai terminé l'effort, toutes les options du produit tiennent dans une seule image qui était inférieure à 8 Ko. En fait, il y avait suffisamment d'espace pour que j'ajoute un UART logiciel de transmission uniquement au code afin que le logiciel puisse diffuser des informations internes qui ont été utilisées pour aider à calibrer les paramètres du produit. La sortie UART était déclenchée lorsqu'un signal 28V était appliqué à l'un des canaux A / N via un diviseur de tension. Le déclencheur était nécessaire car la sortie UART du logiciel utilisait un GPIO qui était normalement un signal qui était une sortie du produit.

Belle histoire.Les compilateurs peuvent faire du bon travail, mais l'effort humain peut y contribuer.De la même manière, j'ai repris un projet utilisant un processeur PIC 8 bits d'un client où différentes versions étaient séparées en trois codes sources similaires - la mémoire du programme était à peu près pleine pour chaque version (mémoire de programme 8k, pleine à environ 85%).J'ai fusionné les codes, optimisé le code C lui-même en vérifiant le code d'assemblage généré et ajouté de nombreuses fonctions au fil du temps (environ 98% de remplissage).Lancer un meilleur compilateur aurait pu aider un peu, mais pas autant que les optimisations de code.
Graham
2019-10-13 01:02:23 UTC
view on stackexchange narkive permalink

La première étape pour tout type d'optimisation est découvrez ce qui le fait .

Votre premier pas devrait être d'amener l'éditeur de liens à vider l'adresse de chaque identifiant de la construction.Ce sont toutes les fonctions et toutes les variables.Votre éditeur de liens devrait également être en mesure de signaler la taille des fonctions;il ne fera probablement pas de tailles variables, mais vous pouvez les déduire de l'adresse de la variable suivante dans la liste.

Une fois que vous savez où va votre espace, vous pouvez faire quelque chose.Il y a de bonnes chances que la solution soit évidente lorsque vous commencerez à regarder ce qui est le plus important.

Jusqu'à ce que vous le sachiez, vous êtes juste aveugle et ce n'est jamais un bon plan.

filo
2019-10-13 15:39:09 UTC
view on stackexchange narkive permalink

Il n'y a pas de moyen "pratique" d'exécuter du code compressé sur un AVR donc votre problème devient "comment optimiser la taille de mon firmware".

Astuces de la chaîne d'outils (c'est-à-dire que vous n'avez pas à modifier votre code):

  1. Quel est le niveau d'optimisation du compilateur? Dans gcc , l'option d'optimisation pour la taille minimale s'appelle -Os

  2. Il existe une fonction appelée optimisation du temps de liaison qui permet d'optimiser davantage la taille. Il est déjà mentionné dans cette réponse.

  3. L'éditeur de liens peut optimiser les données et symboles inutilisés. Il est activé en utilisant -Wl, - gc-sections -ffunction-sections -fdata-sections

  4. Activez -mcall-prologues . Expliqué ici.

Astuces de code générique:

  1. Exécutez nm -S --size-sort -t d your_output_file.elf . Cette commande montrera la taille de chaque symbole (le symbole dans l'éditeur de liens signifie des données ou du code). Vous pouvez alors découvrir où se trouve la plus grande opportunité d'optimisation.

  2. Essayez de trouver des morceaux de code qui pourraient devenir des fonctions à eux seuls.

  3. Évitez d'utiliser printf . Si vous avez juste besoin de convertir des entiers en chaînes, alors itoa est une option. Vous pouvez également consulter xprintf qui est une alternative plus légère au printf standard.

  4. Si vous utilisez des nombres à virgule flottante (float, double) - essayez de convertir le code pour utiliser des entiers. Par exemple, si vous avez besoin de 2 décimales, vous pouvez utiliser une simple mise à l'échelle de 100 (c'est-à-dire que 2,5 devient 250). Toute opération en virgule flottante (aussi simple que float x = a + b ) sur un AVR extrait une pile de code de bibliothèque, car le processeur ne prend pas en charge de telles opérations dans le matériel.

sktpin
2019-10-14 19:50:09 UTC
view on stackexchange narkive permalink

Si vous cherchez des moyens de réduire la taille de votre code de programme - en plus d'avoir le compilateur d'optimisation & en découpant certains, et de ne pas utiliser les fonctions de bibliothèque standard, comme d'autres l'ont noté, cela dépend également de la composition du code du programme quelle sera sa taille.

  • tout d'abord, essayez de trouver un moyen de vous montrer quelles parties de votre programme sont les plus offensantes en termes de taille. Hélas, je ne suis pas familier avec AVR studio, mais ici, l'utilisateur skeeve, post n ° 7 répertorie les tailles de fichiers d'objets uniques. Si votre programme comporte plusieurs modules, vous saurez au moins lesquels d'entre eux sont les plus grands et les bons candidats pour l ' optimisation manuelle .
    • pourquoi la refactorisation manuelle du code, dites-vous? Si vous pouvez réduire la taille du code de cette façon, au lieu d'utiliser l'optimisation du compilateur pour cela, le débogage (comme pour parcourir le code avec GDB ou autre) fonctionnera beaucoup plus bien que lorsque l'optimiseur crée une différence marquée entre le code réel exécuté et votre code source, ce qui rend les choses moins faciles à suivre
  • voici quelques conseils: voir le chapitre "3 conseils et astuces pour réduire la taille du code"
  • éviter le code "copier & coller"
    • c'est-à-dire utilisez une routine pour une séquence d'actions que vous devez effectuer à plusieurs endroits, puis appelez-la à partir de ces endroits
    • cela peut sembler évident ("J'ai utilisé des fonctions, duh!"), mais cela peut valoir la peine de chercher des endroits dans le code qui font presque exactement la même chose, qui font plusieurs étapes à chaque fois, et tout ce qui est différent ne dépend que de quelques paramètres qui pourraient être des arguments de fonction
    • Remarque: il y a aussi d'autres raisons de le faire: utilisez régulièrement des routines
  • (plus désespéré, se battre pour quelques octets supplémentaires ...) envisager de convertir des blocs switch / case plus gros ou if..else..if..else ... (à partir desquels les fonctions sont appelées) en un tableau const avecpointeurs de fonction.Fonctionne uniquement si toutes les fonctions ont la même signature, c'est-à-dire le même type de pointeur, et le calcul d'un index de tableau à partir de la valeur déterminant ce qui est appelé n'ajoute pas beaucoup de code en soi.L'indexation d'un tableau et l'appel sur un pointeur de fonction peuvent produire un code plus petit que beaucoup de switch / case ou if / else pour le même scénario.(c'est le cas pour moi - sur un Microblaze 32 bits, cependant. YMMV)
Dvidunis
2019-10-13 02:34:56 UTC
view on stackexchange narkive permalink

Si votre projet se compose de plusieurs fichiers sources (comme le font la plupart des projets), vous pouvez également consulter Link Time Optimization (généralement abrégé LTO).

Il fait des optimisations supplémentaires entre les objets (au moment du lien, comme son nom l'indique).

Vous pouvez rechercher une option spécifique "Optimisation du temps de liaison" / "LTO" dans votre IDE, ou chercher un emplacement pour ajouter des indicateurs de compilateur.

Si votre compilateur est basé sur GCC ou Clang, vous pouvez ajouter l'indicateur -flto pendant la compilation et le temps de liaison (CFLAGS et LDFLAGS le cas échéant).

Cela peut optimiser des blocs entiers de code (à l'intérieur des fonctions) qui ne sont pas appelés, ou les optimiser pour un modèle d'entrée spécifique.

Notez que si vous utilisez la bibliothèque standard, vous devez vous assurer de la compiler avec LTO pour réaliser de grosses économies.

Vous pouvez en savoir plus sur LTO ici: https://en.wikipedia.org/wiki/Interprocedural_optimization
Et un exemple du noyau Linux ici: https://lwn.net/Articles/744507/



Ce Q&R a été automatiquement traduit de la langue anglaise.Le contenu original est disponible sur stackexchange, que nous remercions pour la licence cc by-sa 4.0 sous laquelle il est distribué.
Loading...