lundi 17 septembre 2018

Chapitre 45 :Clignotement de la led activité du Raspberry avec le timer du BCM2835


Bon ! il faut bien un jour se lancer dans la compréhension et l’exploitation des interruptions. Cela n’a pas été facile mais le projet que je vais vous présenter fonctionne correctement sur mon raspberry(mais j’ai encore de nombreuses interrogations non résolues à ce jour). Nous allons nous servir du timer ARM du BCM2835 (attention il y a plusieurs timers et nous utiliserons celui de la page 196 de la doc du BCM2835) pour générer une interruption à intervalle régulier (environ 3s) pour allumer et éteindre la led activité suivant le même principe que le chapitre précédent.
La théorie est simple !! : le timer génère toutes les 3 secondes une interruption qui sera gérée par un contrôleur d’interruption puis prise en compte par le processeur qui changera de mode de fonctionnement et appellera la bonne routine de traitement de l’interruption.
Donc nous aurons un module qui contiendra les routines du timer, un autre celles du contrôleur d’interruption (IC) un autre la gestion des adresses à appeler suivant le type et le mode (appelé vecteur des interruptions) et bien sûr les modules boot.s et kernel.s comme dans les exemples précédents. Tout le projet est ici.
Dans le module timer, nous trouvons une fonction d’initialisation du timer qui fixe la fréquence, et qui autorise l’envoi d’une interruption. J’ai eu du mal à calculer correctement la fréquence en effet il faut diviser la fréquence de base de l’horloge soit 250 000 000 hertz par le facteur de division +1 (ici 7F  soit 127 + 1 = 128) ce qui donne une fréquence de 1 953 125hz qu’il faut diviser par 256 (indiqué par le code 10 se trouvant sur les bits 2et 3 du registre de contrôle du timer) ce qui donne 7629 hz. Le compteur à une valeur de 22887 ce qui donne un temps de 22887/7629 = 3 secondes
Nous trouvons aussi une fonction de test de l’interruption et de sa remise à zéro.
Dans le module du Contrôleur d’Interruption (IC page 109 doc BCM2835) nous trouvons des fonctions simples d’initialisation, d’activation, de recherche de l’identification de l’interruption et de la fin de l’interruption.
Voyons la partie la plus compliquée : la mise en place du vecteur d’interruption module interruptions.s
Le processeur ARM peut fonctionner dans plusieurs modes : le mode user que nous avons utilisé par défaut dans tous les premiers chapitres de ce blog, le mode superviseur que nous utilisons depuis que nous développons sans OS, puis les modes abort, indéfini, irq et fiq. Pour ces derniers modes, le processeur y passera en fonction du type d’interruptions reçues : instruction non autorisé, problème mémoire, interruptions générés par les périphériques (irq=normale, Fiq= rapide ). Notre timer génère une interruption de type irq.
Pour chaque mode, nous devons prévoir la procédure à appeler lors d’une interruption. Pour cela le processeur se branche sur les adresses en début de la mémoire. Dans notre module, nous commençons donc par recopier une table des adresses des procédures pour tous les modes. Bien sûr pour les modes autre que irq, nous mettons le minimum d’instructions à exécuter (quoiqu’il vaudrait mieux envoyer un message pour indiquer ce qui se passe !!!).
Pour écrire ce module, je me suis servi d’un exemple trouvé dans un livre et cela commence par une instruction que je n’avais jamais vue : mcr p15,0,r0,c12,c0,0 !!!!  Dans la documentation ARM, on trouve d’autres instructions de ce type !! qui servent à piloter des fonctions très particulières du processeur. J’ai mis cette instruction en commentaire car elle est peut être utile pour d’autres processeurs. Ensuite nous trouvons la recopie à l’adresse zéro de la mémoire de la table des adresses d’interruptions puis pour chaque mode la procédure à exécuter. Vous remarquerez qu’il y a une procédure reset qui appellera notre première routine _start.
Vous remarquerez qu’à la place de push et pop, nous utilisons les instructions stmfd et ldmfd qui font la même chose. Remarquez que les adresses de retour au programme appelant sont différentes suivant le mode.
Enfin, nous ne complétons que la fonction irq_handler en appelant les fonctions du contrôleur d’interruptions ou du timer pour gérer l’interruption.
Ce module sera appelé dans le module boot_s car le vecteur d’interruptions doit être chargé au plus tôt.
Ensuite il nous reste à modifier le module kernel.s pour modifier l’allumage et l’extinction de la led ; Pour cela on utilise l’instruction mcr p15,0,r0,c7,c0,4 qui met le processeur en attente d’une interruption. Hélas, je me suis aperçu que cette instruction ne fonctionne qu’en mode superviseur. Si on veut utiliser le mode user, il faudra modifier la partie allumage extinction pour être appelée de la fonction irq_handler ou de la fonction test_interruption_timer. Je vais essayer cela dans le prochain post.
La mise au point de tout cela a été laborieuse car en cas de non fonctionnement, il est pas facile de savoir quelle partie est en cause : est ce le timer qui ne génère pas l’interruption ou le contrôleur qui ne la pas autorisé ou le handler qui fonctionne mal ?  Donc prudence lors des modifications. De plus lors de mes premiers tests, j’ai réussi à planter mon portable sous windows10 (soit 10 minutes au moins pour redémarrer !! ) malgré la liaison série entre les deux. Je pense que la procédure mini_uart d’envoi de caractère ne vérifiant pas la validité des caractères, j’ai du envoyer des cochonneries qui ont fait planter window10 !!!
Mes explications sur le sujet des interruptions étant simplistes, je vous renvoie à toute la documentation sur internet traitant des interruptions des processeurs ARM

mercredi 5 septembre 2018

Chapitre 44: Baremetal : faire clignoter la Led Activité du Raspberry


Maintenant, qu’aucune contrainte de l’OS ne nous bloque, nous pouvons facilement faire clignoter la led d’activité (led verte ). Les autres led du Raspberry modèle 1 ne peuvent pas être utilisées car elles indiquent la présence de l’alimentation électrique, la connexion Ethernet et sa vitesse (10 ou 100 Mbts) et ne sont donc pas programmables.
La led activité est accessible à travers la programmation du pin GPIO N° 16 pour mon raspberry (modèle 1B).
Dans le module kernel.s, nous ajoutons un test sur la commande led pour appeler la routine traitementLed qui allume et éteint la led 5 fois.
Il est possible d’améliorer le programme pour faire clignoter la led à un autre rythme. Le délai d’allumage et d’extinction s’effectue à travers une simple boucle.
 Pour la prochaine fois, je vais essayer de me servir du timer du BCM2835 en faisant appel aux interruptions.
Tout le projet est à récupérer ici 

samedi 1 septembre 2018

Chapitre 43 : Baremetal: affichage des registres et de la mémoire


Dans ce projet, nous allons préparer l’affichage des registres et de la mémoire grâce à la mini uart dont nous avons testé le fonctionnement au chapitre précèdent. En fait il nous suffit de récupérer les procèdures vues dans les premiers chapitres de ce blog et de remplacer les appels système d’affichage par des appels à la procedure d’envoi des chaines par la mini uart.
Nous stockons ces procédures dans le module util2.s et nous en profitons aussi pour écrire une fonction de lecture de chaine de caractère en utilisant la fonction de lecture d’un caractère de la miniuart. Cette fonction restera assez simple et ne prendra pas en compte l’affichage d’un curseur, le déplacement arrière de celui-ci dans la chaine pour insérer un corriger un caractère. Elle ne prend en compte que le retour arrière pour supprimer le dernier caractère saisie. Nous ajoutons aussi une procédure de comparaison de chaine que nous stockons dans le module utilchaine.s
Dans le module kernel.s, nous ajoutons le test des affichages des registres et de la mémoire puis nous créons une boucle de saisie des commandes. Pour cela nous affichons un message d’invite puis appelons la procédure de saisie d’une chaine puis la procèdure de comparaison pour déterminer quelle commande a été saisie. Ici il n’ a que la commande fin de possible.
Nous effectuons aussi une autre modification du fichier linker.ld pour modifier l’adresse de début d’exécution pour la mettre à la valeur 0X8000 , valeur indiquée dans de nombreux exemples sur Internet. Mais attention il faut indiquer au GPU cette nouvelle adresse. Il faut donc dans le fichier config.txt remplacer l’instruction kernel_old=1 par l’instruction kernel_address=0x8000.
Maintenant il ne reste plus qu’à relancer la chaine de compilation par make, corriger les erreurs éventuelles, copier le fichier kernel.img sur la carte SD (attention pas celle de votre linux habituel mais celle préparée au chapitre précèdent), placer cette dernière dans le Raspberry, brancher le câble TTL USB au micro ordinateur, lancer putty connexion série et allumer le Raspberry.
Vous devez avoir ce résultat :



Vous pouvez taper quelques commandes !! pour voir l’affichage mémoire du buffer puis la commande fin pour avoir les messages finaux. Bien sûr vous pouvez les adapter à votre gout.
A l’adresse 80D8, j’ai le vidage du registre d’état avec les 5 premiers bits qui indique le mode actuel du processeur soit la valeur 10011 correspondant au mode superviseur : intéressant non ? Pour les autres affichages on voit que l’adresse de la data et de bss correspondent bien au plan de chargement. Cela va nous faciliter la vie !!
Tout le projet est à récupérer ici.

Chapitre 42 : programmer en assembleur sans OS : partie logiciel


Maintenant les étapes de la partie logicielle :
                Installer les logiciels pour compiler et linker les programmes assembleur ARM sur l’ordinateur à partir du site (ou d’un autre du même type) : https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
                Les programmes sont installés sur le disque C : répertoire Program Files(X86) et donc j’ai préférer recopier les exécutables as, ld, make et objcopy dans un répertoire outils du développement Raspberry.
                Créer un projet test avec comme source un programme assembleur d’un des chapitre précèdent, créer un fichier makefile (à partir d’un exemple) pour générer un exécutable sur l’ordinateur. Transférer cet exécutable sur le raspberry (et bien sûr sous Linux comme vous avez l’habitude) dans un répertoire adhoc et l’exécuter ( suivant l’outil de transfert, il sera peut être nécessaire de modifier les droits par chmod). Si le résultat est correct, bravo votre chaine de compilation sur l’ordinateur est correcte sinon il faut trouver l’erreur et corriger !!! (pensez au nom du processeur !!! vérifier dans le plan de chargement du linker que l’adresse de chargement est 0X10000 Etc etc.)
Tant que la liaison du câble n’est pas assurée (voir chapitre précédent) et que la chaine de compilation ne fournit pas un exécutable correct, il est inutile d’aller plus loin.

Sinon continuer (et toutes les étapes précédentes ne seront plus à refaire):
                Installer dans un répertoire, les fichiers du répertoire début1 (ici) et les modifier éventuellement avec vos propres données suivant le type de Raspberry. Mettre les sources dans un répertoire src et créer un répertoire build.
                Pour ce premier projet nous trouvons un programme boot.s qui effectue l’initialisation à zéro de la zone .bss et l’initialisation du pointeur de la pile. Eh oui sans OS, c’est à nous à tout faire !!!! Ce programme appelle le programme principal kernel_main (kernel.s) qui appelle l’initialisation de la mini uart, l’envoi du message de bienvenue puis qui boucle indéfiniment sur la réception d’un caractère et l’envoi de celui çi par l’uart vers le microordinateur connecté à l’autre bout du câble.
Les programmes util.s, utilgpio.s et mini_uart.s contiennent les fonctions nécessaires à ce petit projet. Les nombreux commentaires (et ceux du tutoriel indiqué dans le chapitre précèdent) permettent de comprendre les instructions. Les fonctions du GPIO sont les mêmes que celles déjà vues dans un chapitre précèdent.
Attention, suivant le modèle du Raspberry, il vous faudra modifier les adresses de base de la mini uart et du gpio contenues dans le fichier des constantes.
                Installer le fichier makeFile.txt et le modifier éventuellement avec les répertoires des outils as, ld, copyobj et le répertoire des sources. (attention suivant le modèle l’image binaire finale s’appelle kernel.img ou kernel7.img ou kernel8.img). Modifier aussi les options de compil avec celles déterminées lors du premier test de la chaine de compilation
                Lancer le tout avec l’utilitaire make (dans le répertoire debut1, il y a un .bat qui effectue ce lancement).
                Corriger les erreurs éventuelles, vérifier la présence du fichier kernel.img et examiner le plan de chargement contenu dans map1.txt. C’est instructif.
                Maintenant il faut préparer la carte SD. Si elle n’est pas formatée, suivre les mêmes instructions que pour la prise en compte de l’installation de base du Rapsberry. Puis n’installer que les fichiers bootcode.bin, config.txt et start.elf nécessaires au boot du GPU.
Le fichier config.txt ne contient que cela :

kernel_old=1
disable_commandline_tags=1
disable_overscan=1
framebuffer_swap=0


L’instruction la plus importante est la première qui va indiquer que l’image du kernel doit être chargé à partir de l’adresse 00000000.
Recopier le fichier kernel.img crée par le make sur la carte SD. Enlever la carte et la mettre dans le Raspberry à la place de votre carte habituelle, connecter le cable TTL USB entre le Gpio du Raspberry et l’ordinateur, lancer putty avec la connexion série telle que définie précédemment, allumer le Raspberry et ……..
Soit ça marche et vous avez le message de bienvenue qui apparait sur l’écran de connexion Putty et vous pouvez taper des caractères sur le clavier de l’ordinateur et qui vont s’afficher sur l’écran de connexion. Bravo !!
Soit rien ne s’affiche et vous avez la led activité qui clignote à un certain rythme : Par exemple 7 éclairs cela veut dire que le GPU n’a pas trouvé le kernel.img (ou le kernel7 ou le kernel8). Donc vous corrigez !!
Soit rien rien du tout et il faut recommencer toutes les vérifications (en particulier les adresses de base du GPIO et la miniUART (attention il y a 2 uarts sur le BCM2835)). Si vous avez modifié les sources des programmes, il faut vérifier la bonne programmation car il n’y a aucune possibilité de savoir ou le programme bugge !!. Ma solution pour la mise au point a été de développer un programme de test sous Linux avec les outils des chapitres précédents mais cela oblige de mapper les registres du GPIO et ceux de la mini uart pour pouvoir y accéder (voir le chapitre sur l’accès au timer ).

Et maintenant que faire ?  D’abord pour arrêter notre programme, il faut éteindre le raspberry car le processeur ne peut pas être arrêté !!!! il boucle sans fin.
Puis modifier le programme kernel.s par exemple le libellé du message, relancer la création de l’image par make, recopier kernel.img sur la carte SD, remettre la carte dans le raspberry, le rallumer et voir si la modification est OK.
Ce sera la seule procédure à appliquer par la suite pour toute évolution future (avec bien sûr brancher le câble TTL USB et lancer Putty connexion série).
Puis il faut continuer en adaptant les procédures d’affichage d’un registre (et de tous les registres) vues dans les chapitres précédents pour remplacer les call system par l’envoi de chaine par la mini uart. Comme cela nous aurons une visibilité de ce qui se passe sur notre raspberry sans Os.

Avec beaucoup d'attention et de vérification,  tout cela fonctionne !!!!!


Chapitre 41 : programmer en assembleur sans OS (baremetal) partie 1


Jusqu’à maintenant, nous avons écrit des programmes en assembleur en utilisant les appels system de Linux pour les fonctions de base. Maintenant, nous allons commencer une série pour ne plus utiliser aucune fonction de l’OS.
Avertissement : Ne vous lancez pas la dedans si vous souhaitez développer rapidement des programmes importants. En effet, sans OS, il vous faudra réécrire en assembleur une grande partie des fonctionnalités offertes par un système d’exploitation complet (ou alors recopier les programmes de ce blog et des programmes trouvés sur internet qui contiennent les fonctionnalités que vous voulez utiliser).
Dans ce blog, nous essaierons d’aller le plus loin possible en récupérant des parties de logiciel déjà écrites sur Internet (ou en s’inspirant fortement) pour voir les difficultés et les contraintes de ce type de développement.
Déjà sans OS, cela veut dire que nous ne pouvons plus afficher quoi que ce soit à l’écran, plus de gestion du clavier à travers le port usb, plus de lecture écriture de la carte SD, plus de connexion ssh et même plus de wifi, bref nous n’avons plus rien !!   Ah si nous avons tous les pins du GPIO de disponibles et la lecture d’une image binaire contenant le ou les programmes et leur exécution lors du démarrage du Raspberry.
C’est pourquoi, les premiers exemples de développement trouvés sur Internet consistent à faire clignoter une LED relié aux pins du GPIO après avoir compilé quelques instructions assembleur ARM et généré une image binaire lisible par le GPU (voir la séquence de boot du raspberry).  Pas très folichon comme début d’expérience.
Voir le site en anglais: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/index.html
Heureusement, mon fils me signale un tutoriel qui explique comment programmer la mini uart du BCM2835, la relier à un ordinateur portable afin d’enclencher une communication entre les 2 ordinateurs. C’est déjà beaucoup mieux.
Bon le tutoriel est en anglais, mélange du C et de l’assembleur ARM, utilise des fonctions des librairies LINUX et concerne le Raspberry modèle 3 !!
Mais rien d’impossible à comprendre !!  donc aller sur : https://github.com/s-matyukevich/raspberry-pi-os
Dressons les étapes de la démarche à suivre pour arriver à notre fin  (remarque ces étapes ne sont à effectuer qu'une seule fois heureusement ) :
                Avoir un raspberry pi !!  (dans mon cas un modèle 1 B 512MO )et un ordinateur (dans mon cas sous Windows 10) qui puisse lire écrire sur une carte SD (ou acheter un petit lecteur de carte SD)
                Installer si ce n’est déjà fait le logiciel Putty sur le PC
                Acheter une carte SD (4GO suffisent car dans l’immédiat on ne sait pas écrire de fichiers sur la carte)
                Acheter un câble de liaison USB TTL   (voir modèle proposé ici https://cdn-learn.adafruit.com/downloads/pdf/adafruits-raspberry-pi-lesson-5-using-a-console-cable.pdf)
                Installer sur l’ordinateur, le driver nécessaire au câble : Attention, avec windows 10 et driver Prolific , j’ai rencontré des problèmes résolus en chargeant le driver du site :
http://www.totalcardiagnostics.com/support/Knowledgebase/Article/View/92/20/prolific-usb-to-serial-fix-official-solution-to-code-10-error
                Rechercher dans le gestionnaire de périphérique le port de l’ordinateur (comX) auquel est rattaché le câble
                En suivant le tutoriel précèdent, activer la console série sur le Raspberry, brancher le câble sur les pins du GPIO, redémarrer le raspberry.
                Sur l’ordinateur lancer putty connexion série, avec le port (comX) trouvé plus haut, et la vitesse 115200 (enregistrer la configuration pour la retrouver ultérieurement)
                Taper <ENTER>  et vous devez vous retrouver avec le login du raspberry pour établir une connexion.
                Sinon, boucler sur les étapes précédentes pour résoudre les problèmes potentiels !!!

Dans le chapitre suivant, nous verrons la partie logiciel. Bon courage.