Question:
Comment implémenter une commutation de pile simple dans les cœurs PIC12 / 16?
abdullah kahraman
2012-06-08 21:26:16 UTC
view on stackexchange narkive permalink

J'essaie de comprendre le fonctionnement des systèmes d'exploitation en temps réel. J'ai regardé les codes sources de certains RTOS. Je veux apprendre en créant mon RTOS simple, quelque chose comme FLIRT.

J'utilise la série PIC16 et le compilateur XC8 C avec MPLABX. Je souhaite également implémenter ce RTOS très simple dans la série PIC12.

J'ai donc décidé de commencer par apprendre à manipuler la pile (comme supercat l'a fait dans cette réponse) et J'ai commencé à chercher et suis tombé sur AN818 qui est intitulé "Manipuler la pile du microcontrôleur PIC18". Cité à partir de la note d'application:

Traditionnellement, la pile de microcontrôleurs n'a été utilisée que comme espace de stockage pour les adresses de retour des sous-programmes ou des routines d'interruption, où toutes les opérations 'push' et 'pop' étaient masquées .

Pour la plupart, les utilisateurs n'avaient pas d'accès direct aux informations sur la pile. Le microcontrôleur PIC18 s'écarte légèrement de cette tradition. Avec le nouveau noyau PIC18, les utilisateurs ont désormais accès à la pile et peuvent modifier le pointeur de pile et empiler les données directement.

Je suis confus. Comment se fait-il qu'il existe des RTOS conçus pour les microcontrôleurs PIC qui fonctionnent avec des cœurs PIC16? Par exemple, OSA RTOS est disponible pour PIC12 / 16 avec le compilateur mikroC.

Pouvez-vous me diriger vers certaines ressources, ou si possible donner des exemples, afin que je puisse en apprendre davantage sur stack changer?

Trois réponses:
supercat
2012-06-08 22:19:58 UTC
view on stackexchange narkive permalink

Chaque RTOS pour un PIC qui n'a pas de pile adressable par logiciel nécessite généralement que toutes les tâches sauf une doivent avoir leur travail divisé en parties ininterrompues qui commencent et se terminent au niveau supérieur de la pile; l'opération "task-yield" n'utilise pas un appel de fonction, mais plutôt une séquence comme

 // Ce code fait partie de la tâche C (supposons pour cet exemple, il existe des tâches // appelées A, B , C movlw JumpC4 & 255 goto TASK_SWITCH_FROM_CTargetC4: 

Ailleurs dans le code, il y aurait du code comme:

 TASK_SWITCH_FROM_A: movwf nextJumpA // Enregistrer l'état de la tâche C // Envoie maintenant l'instruction suivante pour la tâche A movlw TaskB_Table >> 8 movwf PCLATH movf nextJumpB, w movwf PCLTASK_SWITCH_FROM_B: movwf nextJumpB // Enregistrer l'état de la tâche C // Maintenant, envoie l'instruction suivante pour la tâche A movlw TaskC_Table >> 8 movwf PCLATH movf nextJumpC, wKFRwf movwf PCLATH movf nextJumpC, wKFRwf nextJumpC // Enregistrer l'état de la tâche C // Maintenant, envoyez l'instruction suivante pour la tâche A movlw TaskA_Table >> 8 movwf PCLATH movf nextJumpA, w movwf PCL 

À la fin du code, pour chaque tâche, il être une table de sauts; chaque table devrait tenir dans une page de 256 mots (et pourrait donc avoir un maximum de 256 sauts)

TaskC_Table: JumpC0: aller à TargetC0JumpC1: aller à TargetC1JumpC2: aller à TargetC2JumpC3: aller à TargetC3JumpC4: aller à TargetC4 ... etc.

En effet, le movlw au début de la séquence de changement de tâche charge le registre W avec le LSB de l'adresse de l'instruction à JumpC4 . Le code à TASK_SWITCH_FROM_C cacherait cette valeur quelque part, puis enverrait le code pour la tâche A. Plus tard, après l'exécution de TASK_SWITCH_FROM_B , le JumpC4 stocké l'adresse serait rechargée dans W et le système passerait à l'instruction pointée par celui-ci. Cette instruction serait un goto TargetC4 , qui reprendrait à son tour l'exécution à l'instruction suivant la séquence de changement de tâche. Notez que le changement de tâche n'utilise pas du tout la pile.

Si l'on voulait faire un changement de tâche dans une fonction appelée, il pourrait être possible de le faire si l'appel et le retour de cette fonction étaient gérés dans un de manière similaire à ce qui précède (il faudrait probablement envelopper l'appel de fonction dans une macro spéciale pour forcer le code approprié à être généré). Notez que le compilateur lui-même ne serait pas capable de générer du code comme ci-dessus. Au lieu de cela, les macros dans le code source généreraient des directives dans le fichier en langage assembleur. Un programme fourni par le fournisseur RTOS lirait le fichier en langage d'assemblage, rechercherait ces directives et générerait le code de vectorisation approprié.

Je ne pouvais pas comprendre le code sauf à partir de la partie de réglage PCLATH. Que sont JUMPCx et TargetCx? Que fait le déplacement de `JumpC4 & 255` vers W?
Merci pour la modification! Ce serait mieux si vous m'en aviez remarqué :) Autant que je sache, à chaque rendement d'une tâche, il y a une cible de retour à la table de la tâche. C'est ce que je fais maintenant en C, quelque chose de très similaire à [this] (http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html). Je ne comprends pas pourquoi il y a un besoin pour les tables? [Cette] page (http://www.piclist.com/techref/microchip/multitasking.htm#cooperative) montre que cela ne peut être fait qu'en utilisant une variable par tâche, ou deux s'il y a pagination.
Il existe un certain nombre d'approches légèrement différentes que l'on peut utiliser pour réaliser le multitâche sur le PIC. L'avantage de l'approche par table de sauts est qu'elle utilise des variables à un octet pour suivre la prochaine étape de chaque tâche, mais permet toujours aux tâches elles-mêmes d'utiliser 2K de code. Si le code de chaque tâche tient dans une page de 256 octets, on peut renoncer à la table de saut. Alternativement, si l'on ne veut pas utiliser la table de saut, on peut utiliser des variables à deux octets pour la «prochaine étape» de chaque tâche, mais le code de changement de tâche devient plus lent et plus volumineux.
Quant à l'utilisation de «movlw / goto» contre «retlw», ce dernier économise un mot d'espace de code par tâche-commutateur, mais finit par être un cycle plus lent à cause de la nécessité d'appeler un «tremplin». Plus important encore, lors de l'utilisation de `movlw / goto`, il est possible pour une tâche d'effectuer des changements de tâches à partir de routines imbriquées; si l'on limitait l'utilisation de `retlw` à des tâches qui n'avaient pas besoin de basculer à partir de routines imbriquées, ce serait bien, mais mélanger les paradigmes de changement de tâche pourrait ajouter de la confusion.
Olin Lathrop
2012-06-09 00:22:20 UTC
view on stackexchange narkive permalink

Les PICs traditionnels de 14 bits (principalement PIC 16 et quelques PIC 12) n'ont pas de moyen pour le programme d'accéder à la pile d'appels. Cela rend donc impossible un véritable système multi-tâches.

Cependant, vous pouvez toujours avoir ce que j'appelle des pseudo tâches. Il s'agit d'une routine qui est appelée périodiquement à partir de la boucle d'événements principale. Il gère une adresse de redémarrage en interne. Lorsque la boucle paire principale l'appelle, elle saute à l'adresse de redémarrage de la pseudo tâche privée de ce module. Après un certain traitement, le code de tâche appelle une macro YIELD qui définit l'adresse de redémarrage immédiatement après la macro, puis revient à partir de l'appel d'origine. Comme la pile d'appels ne peut pas être déroulée, YIELD ne peut être appelé qu'à partir du niveau de tâche supérieur. C'est quand même une abstraction utile.

Vous pouvez voir un exemple d'une telle pseudo tâche utilisée pour traiter un flux de commandes à partir d'un ordinateur hôte dans mon module de modèle QQQ_CMD.ASPIC. Dans ce cas, le retour à l'appelant est masqué dans la macro GETBYTE, qui semble aller chercher l'octet d'entrée suivant du point de vue de la tâche. Installez ma version des outils de développement PIC à partir de la page de téléchargement de logiciels. QQQ_CMD.ASPIC sera dans le répertoire SOURCE> PIC dans le répertoire d'installation du logiciel.

Pour voir comment un véritable multitâche peut être effectué lorsque vous avez accès à la pile d'appels, jetez un œil à TASK.INS .ASPIC dans le même répertoire. C'est mon système multitâche générique pour le PIC 18. Vous pouvez voir la même chose pour l'architecture dsPIC en regardant TASK.INS.DSPIC dans le répertoire SOURCE> DSPIC.

Il semble que je doive commencer à apprendre l'assemblage.
@abdullah: Oui, vous avez besoin du langage d'assemblage pour effectuer des actes non naturels sur la pile, ce qui est nécessaire dans la plupart des schémas de commutation de tâches. Vous devez au moins comprendre l'assemblage de toute façon pour savoir ce qui se passe dans les bas niveaux, même si vous programmez dans un langage de haut niveau. Sur les microcontrôleurs, vous ne pouvez pas ignorer les détails matériels. Ce n'est pas comme sur un grand système à usage général où il y a de nombreuses couches d'abstraction entre vous et le matériel. Tous les EE utilisant des microcontrôleurs * doivent * connaître l'assemblage.
Je pense que je n'ai pas à mémoriser toutes les instructions et ainsi de suite, elles sont toutes dans la fiche technique. Ce que je dois comprendre, ce sont les détails du matériel. J'ai regardé la macro `GETBYTE` mais je n'ai rien compris du tout. J'y reviendrai plus tard. Maintenant, je regarde [Pic'Xe] (http://picxe.sourceforge.net/) et je peux comprendre l'assemblage quand je regarde la fiche technique. Cependant, je ne comprends pas ce que signifient FSR et INDF, etc., qui sont du matériel. Donc, je suis maintenant les tutoriels d'architecture PIC x14 [ici] (http://www.pictutorials.com/Flash_Tutorials.htm).
@abdullah: Quelle partie de GETBYTE ne comprenez-vous pas? Je peux le parcourir, mais des questions spécifiques aideraient. Notez que mon code suppose les macros définies dans STD.INS.ASPIC et est également exécuté via mon préprocesseur (PREPIC, décrit dans le répertoire DOC) avant que MPASM ne le voit. Je viens de regarder GETBYTE, et il utilise certaines de mes macros (DBANKIF, JUMP, etc.), mais pas le préprocesseur.
Ah, il m'est vraiment trop difficile de comprendre GETBYTE. Cependant, ce que je comprends de la macro YIELD, c'est qu'elle enregistre l'adresse de redémarrage de la fonction, qui se trouve immédiatement après la macro, dans la pile de la fonction. Lorsqu'un déclencheur de commutation de tâche se produit, il charge le PC avec l'adresse de redémarrage de la tâche suivante. Ce que je comprends, c'est que si je n'ai besoin d'aucune commutation de tâche dans les sous-routines, ce que je n'ai jamais fait, alors c'est effectivement la même technique avec la commutation de pile.
Maintenant, si j'ai bien compris le concept de «pseudo tâches», je devrais trouver des moyens de le faire fonctionner avec le strict minimum, puis de l'adapter à C, car je suis désespéré avec l'assembleur. Il me faudra des mois pour me familiariser avec l'assembleur. Une chose que j'ai remarquée est que l'assembleur est bien plus efficace que le C, mais il me faudra des semaines pour finir d'écrire un programme avec l'assembleur, alors qu'avec C cela me prendra des heures.
@abdullah: Quelle macro RENDEMENT? J'ai scanné tout mon code dans le répertoire source PIC et la seule macro RENDEMENT que j'ai pu trouver était dans NET_DHCP.INS.ASPIC. C'est en fait un très bon exemple de pseudo-tâche, mais je ne l'ai pas mentionné auparavant parce que j'avais oublié qu'elle était là. Vous aurez besoin d'un assembleur pour implémenter une pseudo-tâche car elle nécessite de passer à une adresse arbitraire stockée dans une variable. Vous êtes censé être un EE, alors allez apprendre l'assembleur.
Eh bien, en fait, je faisais référence à la macro YIELD dans votre réponse. J'ai eu une classe dans ma dernière année (j'ai obtenu mon diplôme l'année dernière) à l'université sur 8086 assembleur et j'ai obtenu un A à ce sujet. Je suis donc familier avec l'assembleur. J'ai récemment entendu parler de l'assembleur PIC et j'ai compris tout le code de cette page (http://www.piclist.com/techref/microchip/multitasking.htm#cooperative), enfin :). C'est en fait ce que je veux implémenter dans C.
En passant, j'ai regardé les fichiers de liste qui sont générés par mon compilateur et oh, cher! Cela crée essentiellement du désordre!
davidcary
2012-06-09 08:27:57 UTC
view on stackexchange narkive permalink

Les puces "noyau 14 bits" de la série PIC16 ont une "RAM de pile matérielle" qui est complètement séparée et indépendante de la "RAM de données". (Elle a même une taille et une largeur différentes de celles de la RAM de données). diagramme "pour l'une de ces puces montre clairement que la" pile matérielle "est connectée uniquement au compteur de programme - la seule instruction qui écrit dans cette pile matérielle est (les différents types de) CALL, et la seule instruction qui lit depuis cette pile matérielle est (les différents types de) RETURN.

La "commutation de pile" est impossible pour la "pile matérielle" de ces puces "noyau 14 bits".

Cela rend la plupart méthodes traditionnelles de commutation de tâches du système d'exploitation impossibles sur cette série de puces.

Il y a quelques RTOS qui fonctionnent sur cette série de puces, il doit donc y avoir quelques méthodes de commutation de tâches possibles sur ce puce ( "Méthodes multitâches spécifiques au microcontrôleur PIC").

En pratique, tous les RTOS que j'ai vus ont une sorte de commutateur de tâches coopératif qui "cède" d'une tâche à la suivante uniquement dans le code de niveau supérieur de chaque tâche. Parce que le rendement ne provient jamais d'un sous-programme, il n'y a pas d'adresse de retour sur la pile matérielle.

En principe, il est possible de programmer ces puces pour construire une pile logicielle dans la RAM et de l'utiliser pour simuler une pile de retour (en utilisant une "pile définie par l'utilisateur" dans la RAM de données, plutôt que la pile matérielle). utiliser tous les éléments de commutation de tâches traditionnels du système d'exploitation, comme donner à chaque tâche un bloc de RAM séparé pour sa pile locale, etc. je ne sais pas si la complexité supplémentaire et les séquences d'appels non standard en valent la peine.

Merci pour les informations générales et le lien. Le lien a beaucoup aidé.


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 3.0 sous laquelle il est distribué.
Loading...