dimanche 30 septembre 2018

Chapitre 47 : Baremetal : lire un fichier sur la carte SD. Partie 1


Maintenant, nous allons nous attaquer à un gros morceau : la lecture d’un fichier de la carte SD en assembleur. En effet, si nous voulons continuer à écrire des programmes en assembleur sans faire appel à Linux, il nous faut arriver à lire et à écrire des données sur le seul support fixe dont nous disposons : la carte SD.
Ce projet va se décomposer en 3 parties : la lecture de bas niveau d’un bloc de données de la carte, l’exploitation des données des répertoires pour trouver l’emplacement du fichier, puis la lecture du fichier lui-même.
Pour la première partie, nous allons nous servir du périphérique EMMC du BCM2835. Hélas, la documentation de ce périphérique n’est pas complète et renvoi à un document de la société Arasan qui n’est pas disponible sur le net.
Heureusement, le consortium qui définit l’utilisation des cartes SD publie une documentation importante sur les protocoles à utiliser. Donc grâce à la doc du BCM2835, des docs de la carte SD, et d’exemples de programmes en C trouvés sur le net, je suis arrivé à écrire des routines qui fonctionnent plus ou moins. Pourquoi le moins : parce qu’il y a une grande variété de cartes, de commandes, d’états, de possibilités qu’il m’est impossible de tester tout cela sur mon raspberry avec un seul type de carte. Ce qui veut dire que ces routines seront à adapter suivant vos processeurs et type de cartes et les actions que vous souhaiteriez développer.
Vous me pardonnerez aussi l’imprécision de mes explications, mais certains points me restent encore obscurs !!!
Tout le projet est ici.
Dans cette première partie, nous allons lire un bloc de données brut de la carte SD à l’aide de routines écrites dans le module carteSD.s. Pour cela nous devons procéder à l’initialisation de la carte qui va se décomposer aussi en 3 parties : l’initialisation des pins du GPIO, l’initialisation de l’horloge et l’initialisation de la carte à l’aide de commandes particulières. En effet, une carte SD possède un micro contrôleur qui va dialoguer avec le driver maitre EEMC à l’aide de commandes que nous allons envoyer pour attendre une réponse qu’il faudra analyser.
Comme nous avons pu le voir précédemment pour le GPIO ou le timer, les périphériques sont gérés à partir de registres (en fait des zones particulières de 32 bits situées à des adresses bien particulières et indiquées dans la doc du BCM2835). Ces registres seront soit alimentés par nos routines pour lancer une commande avec ses arguments, soit lus pour déterminer l’état du périphérique (ok, en attente ou en erreur) ou récupérer (ou écrire) des données. Mais attention, les réponses du périphérique ne sont pas synchrones et il faudra souvent prévoir un temps d’attente entre l’écriture dans un registre de commande et la lecture de la réponse. Pour vérifier le contenu de ces registres, j’ai écrit une fonction dans le module util.s qui affiche le contenu en hexa et binaire.
Commençons à attribuer la fonction 0b111 aux pins du GPIO pour la liaison avec la carte SD :
Pin 47 :   pin CD de la carte (Card Detect) (voir la documentation SD.pdf)
Pin 48 :  pin clk (Clock)
Pin 49 : pin CMD (command)
Pin 50, 51, 52 et 53 : pin Dat0 Dat1 Dat2 Dat3
Ensuite on met à jour les registres GPPUD et GPPUDCLK1 en suivant la démarche indiquée dans la doc BCM2835. Je n’arrive pas trop à comprendre à quoi ça sert !!!!!
Nous récupérons le numéro de slot de la carte SD dans le registre EMMC_SLOTISR_VER et nous le conservons en mémoire. Puis nous effectuons un reset de la carte SD en positionnant les valeurs adhoc dans les registres EMMC_CONTROL0 et EMMC_CONTROL1. Et nous trouvons une séquence que nous allons retrouver plusieurs fois. Il s’agit d’une boucle d’attente qui teste les codes retour attendus. Si ces codes ne sont pas trouvés au bout d’un certain temps, une erreur est signalée. Un code retourné peut aussi signaler une erreur ou un problème de la carte.
Nous passons maintenant à un réglage de l’horloge à 400Khz. Je vous laisse le soin de regarder le code en détail mais qui est assez compliqué. Puis nous attaquons toute la séquence d’initialisation de la carte SD avec le lancement de plusieurs commandes et la vérification de leur bonne fin. Pour le rôle de chaque commande, je vous renvoie à la documentation de base sd.pdf. Attention, j’ai copié le nom des commandes d’un programme en C et ce nom ne correspond pas à celui de la documentation. Je l’ai ajouté pour certaines en commentaire.
Vous remarquerez l'utilisation de l'instruction tst qui permet de tester certains bits d'un registre sans le modifier (c'est comme un and sans modification)  mais attention à ne pas inverser le test : si les bits sont présents il faut utiliser un bne (not equal) et pas un beq !!
Au milieu de cet enchainement, il faut rechanger la fréquence d’horloge pour la passer à 25Mhz mais sur mon raspberry, cela entraine des erreurs sur les commandes suivantes. J’ai dû me contenter de mettre 20Mhz comme fréquence pour avoir un résultat sans erreur.
Lors du retour de cette routine d’initialisation, il faut tester la valeur du registre r0 pour arrêter la suite du programme en cas d’erreur.
Ici après cette séquence, nous nous contentons de lire le seul bloc 0 avec la routine lecture_bloc_sd et de stocker les données dans un buffer en mémoire. Nous affichons ce buffer à partir du 500 caractère pour vérifier que la marque du système de fichier est bien un fat32 ( code  0x55AA à l’adresse 0x1FE).
Remarque importante : il est possible de lire plusieurs blocs de 512 caractères d’affilée. La documentation précise que dans ce cas, il faut terminer la lecture par l’envoi de la commande CMD_STOP_TRANS. Lors de mes tests, cette commande est systématiquement en erreur sans que je trouve pourquoi !! je l’ai donc mise en commentaire car les blocs sont lus correctement.

Aucun commentaire:

Enregistrer un commentaire