10
votes

Exécution de code dans les systèmes embarqués

Je travaille dans le domaine système intégré. J'aimerais savoir comment un code est exécuté à partir d'un microcontrôleur (UC ne doit pas être subjectif, en général), à partir d'un fichier C. De plus, j'aimerais connaître des trucs comme le code de démarrage, le fichier d'objet, etc. Je ne pouvais pas trouver de documentations en ligne concernant les choses ci-dessus. Si possible, veuillez fournir des liens qui expliquent ces choses à partir de zéro. Merci d'avance pour votre aide


3 commentaires

Cela aiderait à indiquer quel type de microcontrôleur.


Je travaille dans 8051 Controller. Je connais un peu comment les opcodes sont récupérés et exécutés dans la langue d'assemblage). Mais voudrait savoir comment un projet avec plusieurs fichiers C, exécute sur un UC.


Les fichiers C n'exécutent pas! :-) Ils sont compilés pour des fichiers d'objet et sont liés à une image exécutable finale, soit chargée pour clignoter, soit par la RAM et s'exécuter à partir de là.


7 Réponses :


5
votes

Généralement, vous travaillez à un niveau de beaucoup plus bas que les ordinateurs à usage général.

Chaque processeur aura certainement un comportement sur la mise sous tension, telle que le nettoyage de tous les registres et la définition du compteur de programme sur 0xF000 (tout est non spécifique, de même que votre question).

L'astuce consiste à assurer que votre code est au bon endroit.

Le processus de compilation est généralement similaire aux ordinateurs à usage général en ce que vous traduisez C en code de machine (fichiers d'objet). À partir de là, vous devez relier ce code avec:

  • votre code de démarrage système, souvent en assembleur.
  • toute bibliothèques d'exécution (y compris les bits requis du C RTL).

    Le code de démarrage du système n'initialise généralement que le matériel et définit l'environnement afin que votre code C puisse fonctionner. Les bibliothèques d'exécution dans des systèmes embarqués rendent souvent la grosse substance volumineuse (comme support de point flottant ou printf) en option afin de garder le cochon de code.

    La liaison dans les systèmes embarqués est également généralement plus simple, émettant de code de localisation fixe plutôt que des fichiers binaires relocatables. Vous l'utilisez pour vous assurer que le code de démarrage va à (par exemple) 0xf000.

    Dans les systèmes embarqués, vous souhaitez généralement que le code exécutable soit présent depuis le début afin que vous puissiez le graver dans EPROM (ou EEPROM ou un autre périphérique qui maintient le contenu sur la mise sous tension).

    Bien sûr, gardez à l'esprit que mon dernier inconvénient était avec des processeurs 8051 et 68302. Il se peut que les systèmes «embarqués» de nos jours sont des boîtes de Linux pleines épouses avec toutes sortes de matériel merveilleux, auquel cas il n'y aurait pas de différence réelle entre le but général et intégré.

    Mais j'en doute. Il y a toujours besoin de matériel sérieusement à faible spécification qui nécessite des systèmes d'exploitation personnalisés et / ou du code d'application.

    SPJ Embedded Technologies a un évaluation téléchargeable de leur environnement de développement 8051 qui cherche à être ce que vous voulez. Vous pouvez créer des programmes jusqu'à 2 000 de taille, mais il semble passer à travers l'ensemble du processus (compilation de liaison, génération de fichiers heex ou bin pour le dumping sur le matériel cible, même un simulateur qui donne accès aux trucs et périphériques externes sur puce. ).

    Le produit de non-évaluation coûte 200 euros mais, si tout ce que vous voulez est un peu de jeu, je téléchargerais simplement l'évaluation - autre que la limite 2K, c'est le produit complet.


1 commentaires

Merci pour la réponse rapide, Pax. Si possible, pouvez-vous essayer de fournir de bons liaisons disponibles pour expliquer le processus ci-dessus (DOENST MATERIQUE QUE L'UC réelle est)



1
votes

J'ai de l'expérience avec les microcontrôleurs AVR, mais je pense que cela sera à peu près la même chose pour tous:

La compilation va dans les mêmes lignes que d'un code C normal. Il est compilé dans les fichiers d'objets, ceux-ci sont liés ensemble, mais au lieu de sortir du format complexe comme ELF ou PE, la sortie est simplement placée sur une adresse fixe dans la mémoire de l'UC sans aucun en-tête.

Le code de démarrage (si le compilateur génère) est ajouté de la même manière que le code de démarrage pour les ordinateurs "normaux" - il y a un code ajouté avant votre code principal (et peut-être aussi après).

Une autre différence est la liaison - tout ce qui doit être liée statiquement, car les microcontrôleurs n'ont pas de système d'exploitation pour gérer la liaison dynamique.


2 commentaires

Merci cube. Je comprends maintenant qu'un exécutable sera créé dans le PC hôte et sera mis en mémoire non volatile de l'UC. J'aimerais savoir comment l'exécution réelle commence par la cible réelle par la suite. Toute documentation en ligne REG Ceci ou une étude de cas est préférable.


Pas assez précis sur elf / pe. De nombreux liants pour les systèmes embarqués de sortie Elf, il est juste que le code binaire de celui-ci est une adresse fixe et non indépendante de la position. Ainsi, il est possible de générer un fichier hexagonal (Motorola S-Record ou Intel Hex) ou une décharge binaire droite (en supposant que vous connaissez l'adresse de départ) pour charger dans Flash.



45
votes

Être un architecte à microprocesseur, j'ai eu la possibilité de travailler à un niveau très bas pour les logiciels. Fondamentalement, l'embarquage de bas niveau est très différent de la programmation générale PC uniquement au niveau spécifique du matériel.

Le logiciel embarqué de bas niveau peut être décomposé dans les éléments suivants:

  1. réinitialiser le vecteur - ceci est généralement écrit en montage. C'est la toute première chose qui fonctionne au démarrage et peut être considérée comme un code spécifique au matériel. Il effectuera généralement des fonctions simples telles que la configuration du processeur dans un état stable prédéfini en configurant des registres et tels. Ensuite, il va passer au code de démarrage. Le vecteur de réinitialisation le plus basique saute simplement directement au code de démarrage.
  2. code de démarrage - il s'agit du premier code spécifique à un logiciel qui fonctionne. Son travail est essentiellement pour configurer l'environnement logiciel afin que le code C puisse fonctionner sur le dessus. Par exemple, le code C suppose qu'il existe une région de mémoire définie comme pile et tas. Ce sont généralement des constructions logicielles au lieu du matériel. Par conséquent, cette pièce de code de démarrage définira les pointeurs de pile et les pointeurs de démarrage et tels. Ceci est généralement groupé sous le ' C-Runtime '. Pour le code C ++, les constructeurs sont également appelés. À la fin de la routine, il exécutera principal () . EDIT: Variables qui doivent être initialisés et certaines parties de la mémoire nécessitant une compensation sont effectuées ici. Fondamentalement, tout ce qui est nécessaire pour déplacer les choses dans un «état connu».
  3. Code d'application - Il s'agit de votre application C réelle en train de partir de la fonction principale () . Comme vous pouvez le constater, beaucoup de choses sont réellement sous le capot et se produisent même avant que votre première fonction principale soit appelée. Ce code peut généralement être écrit comme matériel-agnostique s'il existe une bonne Couche d'abstraction matérielle disponible. Le code d'application utilisera définitivement une grande quantité de fonctions de la bibliothèque. Ces bibliothèques sont généralement liées statiquement dans des systèmes embarqués.
  4. Bibliothèques - Ce sont vos bibliothèques C standard C offrant des fonctions C primitives. Il existe également des bibliothèques spécifiques à un processeur qui mettent en œuvre des éléments tels que le support logiciel de point flottant. Il peut également y avoir des bibliothèques spécifiques au matériel pour accéder aux périphériques d'E / S et tels que STDIN / STDOUT. Un couple de bibliothèques C communes sont NewLib et uclibc .
  5. interruption / exception gestionnaire - ce sont des routines qui fonctionnent à des heures aléatoires lors de l'exécution du code normal à la suite de changements d'états matériels ou de processeurs. Ces routines sont également typiquement écrites en montage car elles doivent fonctionner avec un minimum de frais généraux afin de servir le matériel actuel appelé.

    J'espère que cela donnera un bon départ. N'hésitez pas à laisser des commentaires si vous avez d'autres questions.


9 commentaires

Bang sur la cible !! Merci sybréon. Maintenant, qu'en est-il de l'allocation de mémoire? En supposant que mon UC a une mémoire flash, les variables statiques et globales utilisées par le programme seraient stockées dans le code de démarrage du système, les variables locales dans la pile (à nouveau RAM) et le code reste en flash (ROM ). L'exécution réelle se produit en exécutant chaque instruction de Flash. Ai-je raison?


@Guru_newbie: "L'exécution réelle se produit en exécutant chaque instruction de Flash." Certains processeurs exécutent le code directement à partir de Flash et certains ne le font pas. Je crois que le 8051 exécutera le code de Flash. Des processeurs embarqués d'extrémité supérieure (32 bits), comme un PC copiera le code d'application en RAM et exécutera de la RAM.


@sybreon: l'étape 2 configurez également des variables statiques en RAM et copier également les données d'initialisation.


@simon - Oui, c'est généralement le cas. Parfois, un drapeau du compilateur peut contrôler des choses comme la zéro .bbs et ce qui ne traite pas.


Pour ajouter sur la mémoire de la mémoire, cela est généralement contrôlé par la liaison. Si la section .Text est configurée en tant que flash, c'est là que viendra le code.


Je pense que l'inquisiteur peut signifier une allocation de mémoire d'exécution dynamique, comme Malloc. Si tel est le cas, cette affaire est dans la section "Bibliothèques" et est souvent appelée "Les bibliothèques C standard C", dont NewLib et UCLIBC sont des implémentations particulières des bibliothèques C standard C, destiné à une utilisation du microcontrôleur.


J'aime que vous répondiez Sybreon, mais (au point 2), je ne pense pas que c suppose qu'il y aura un tas. De nombreux compilateurs C intégrés aurèvent la mémoire pour les variables statiques et alloueront de l'espace pour les variables locales de la pile. Dans ce cas, le tas ne entrerait en jeu que si vous utilisiez des bibliothèques avec MALLOC (). Pas sûr des constructeurs C ++.


@Ben Gartner mais MALLOC () fait partie de la bibliothèque standard C qui fait partie de C. Par conséquent, si cette bibliothèque suppose qu'il y a un tas, c'est donc un dans son ensemble. Si vous ignorez cette bibliothèque que vous parlez d'un «sous-ensemble» de C.


@Warrenp dans le code d'application qui dans la mémoire du processeur Il n'y a pas de fonction principale (), elle est représentée en tant que fichier hexadécimal, ma question à ce respect comment le processeur sait où commencer l'exécution du code.



3
votes

J'ai l'impression que vous êtes plus intéressés par ce que les appels sybreon « étape 2. » Beaucoup peuvent y arriver, et il varie considérablement selon la plate-forme. Habituellement, ce genre de choses est assurée par une combinaison de bootloader, trousses de bord, C Runtime (CRT), et si vous avez un, le système d'exploitation.

En règle générale, après le vecteur remise à zéro, une sorte de bootloader exécutera de la mémoire flash. Ce bootloader pourrait tout simplement mettre en place le matériel et sauter dans CRT de votre application, également en flash. Dans ce cas, le CRT serait probablement effacer le .bss, copiez le .data à la RAM, etc. Dans d'autres systèmes, le bootloader peut disperser-charger l'application à partir d'un fichier codé, comme un ELF, et le CRT met tout en place d'autres des vêtements d'exécution (tas, etc.). Tout cela se produit avant que le CRT appelle principal () de l'application.

Si votre application est lié statiquement, les directives de l'éditeur de liens spécifier les adresses où .data / .bss et la pile sont initialisés. Ces valeurs sont soit liés dans le tube à rayons cathodiques ou codés dans le ELF. Dans un environnement liée dynamiquement, l'application de chargement est habituellement assurée par un système d'exploitation qui re-cibles ELF pour fonctionner dans tout ce mémoire désigne le système d'exploitation.

En outre, certaines cibles exécuter des applications de flash, mais d'autres copieront le .text exécutable de flash RAM. (Ceci est généralement un compromis entre vitesse / encombrement, puisque la RAM est plus rapide / plus large que le flash sur la plupart des cibles.)


0 commentaires

1
votes

Vous pouvez jeter un coup d'œil sur le très détaillé Tutoriel de bras GNU par Jim Lynch.


1 commentaires

Il ne s'agit que de noter que le bras est parmi les systèmes intégrés plus complexes. Le code de démarrage est particulièrement complexe par rapport aux plus petits UCS E.G. AVR.



2
votes

OK, je vais donner une photo ...

Tout d'abord d'architectures. Von Neumann vs Harvard. Harvard Architecture a une mémoire séparée pour le code et les données. Von Neumann ne le fait pas. Harvard est utilisé dans de nombreux microcontrôleurs et c'est ce que je connais.

Donc, à commencer par votre architecture de base de Harvard, vous avez la mémoire de programme. Lorsque le microcontrôleur démarre d'abord, il exécute les instructions de l'emplacement de mémoire zéro. Habituellement, il s'agit d'une commande de saut à l'adresse où le code principal commence.

Maintenant, quand je dis des instructions, je veux dire des opcodes. Les opcodes sont des instructions codées dans des données binaires - généralement 8 ou 16 bits. Dans certaines architectures, chaque opcode est codé dur pour signifier des choses spécifiques, dans d'autres, chaque bit peut être significatif (c.-à-d. Bit 1 moyen de vérifier le port, le bit 2 signifie un drapeau zéro, etc.). Donc, il y a des opcodes puis des paramètres pour les opcodes. Une instruction de saut est un opcode et une adresse mémoire 8 ou 16 ou 32 bits que le code "saute". C'est-à-dire que le contrôle est transféré dans les instructions à cette adresse. Cela l'accomplit en manipulant un registre spécial contenant l'adresse de la prochaine instruction à exécuter. Donc, pour passer à l'emplacement de la mémoire 0x0050, il remplacerait le contenu de ce registre avec 0x0050. Dans le cycle d'horloge suivant, le processeur lirait le registre et localiserait l'adresse de la mémoire et exécuterait l'instruction là-bas.

Les instructions d'exécution provoquent des modifications de l'état de la machine. Il existe un registre général d'état général qui enregistre des informations sur la dernière commande (c'est-à-dire si elle est une addition alors s'il y avait une exécution requise, il y a un peu pour cela, etc.). Il y a un registre «accumulateur» où le résultat de l'instruction est placé. Les paramètres des instructions peuvent soit aller dans l'un des registres à usage général, soit l'accumulateur, ou dans des adresses de mémoire (données ou programme). Différents OPCODES ne peuvent être exécutés que sur des données à certains endroits. Par exemple, vous pourrez peut-être ajouter des données de deux registres à usage général et faire apparaître dans l'accumulateur, mais vous ne pouvez pas prendre de données à partir de deux emplacements de mémoire de données et que le résultat apparaît dans un autre emplacement de mémoire de données. Vous devez déplacer les données que vous souhaitez utiliser les registres à usage général, faire l'ajout, puis déplacez le résultat à l'emplacement de la mémoire souhaité. C'est pourquoi l'assemblage est considéré comme difficile. Il existe autant de registres de statut que l'architecture est conçue pour. Des architectures plus complexes peuvent avoir plus pour permettre des commandes plus complexes. Les plus simples peuvent ne pas.

Il y a aussi une zone de mémoire connue sous le nom de pile. C'est juste une zone en mémoire pour certains microcontrôleurs (comme le 8051). Dans d'autres, il peut avoir des protections spéciales. Il existe un registre appelé un pointeur de pile qui enregistre quel emplacement de mémoire le «haut» de la pile est à. Lorsque vous "poussez" quelque chose sur la pile de l'accumulateur, l'adresse de mémoire "top" est incrémentée et les données de l'accumulateur sont placées dans l'ancienne adresse. Lors de la récupération ou de la sortie de données de la pile, l'inverse est effectué et les pointeurs de pile sont décréments et les données de la pile sont placées dans l'accumulateur.

Maintenant, j'ai aussi une sorte de vitrage sur la manière dont les instructions sont "exécutées". Eh bien, c'est quand vous descendez à la logique numérique - VHDL Type de choses. Les multiplexeurs et les décodeurs et les tables de vérité et tels. C'est le vrai gritty nitty de design - genre de. Donc, si vous souhaitez "déplacer" le contenu d'un emplacement de mémoire dans l'accumulateur, vous devez comprendre la logique d'adressage, effacer le registre de l'accumulateur et les données à l'emplacement de la mémoire, etc. Il est intimidant quand il est placé ensemble ensemble mais si Vous avez fait des parties séparées (comme l'adressage, un demi-additionneur, etc.) dans VHDL ou de toute mode logique numérique, vous pourriez avoir une idée de ce qui est nécessaire.

Comment se rapporte-t-il à C? Eh bien, un compilateur prendra les instructions C et les transformera en une série d'opcodes exécutant les opérations demandées. Tout cela est essentiellement des données hexagonales - ses et des zéros qui sont placés à un moment donné dans la mémoire du programme. Ceci est fait avec les directives compilatrice / lieur qui indiquent quel emplacement de mémoire est utilisé pour quel code. Il est écrit dans la mémoire flash sur la puce, puis lorsque la puce redémarre l'emplacement de la mémoire de code 0x0000 et passe à l'adresse de démarrage du code dans la mémoire du programme, puis commence à brancher OPCODES.


1 commentaires

Lors de la réinitialisation, le processeur commence l'exécution au vecteur de redémarrage qui peut être ou non à l'emplacement 0x0000. Vous devez regarder la fiche technique des processeurs spécifique pour l'emplacement du vecteur de redémarrage.



2
votes

Vous pouvez vous reporter au lien https://automotivetechis.wordpress.com/ .

La séquence suivante aperçu la séquence des exécutions d'instructions du contrôleur:

1) attribue la mémoire principale de l'exécution du programme.

2) Copie l'espace d'adressage de la mémoire secondaire à la mémoire principale.

3) copie les sections .Text et .Data de l'exécutable dans la mémoire principale.

4) Copie les arguments du programme (par exemple, arguments de ligne de commande) sur la pile.

5) Initialise registres: Définit l'ESP (pointeur de pile) pour pointer vers le haut de la pile, efface le reste.

6) saute pour commencer la routine, qui: copie les arguments principaux () de la pile et saute à la main ().


0 commentaires