Florent de LAMOTTE
12 septembre 2002
Cette documentation présente les différents outils utilisés lors des phases de développement et de tests du code de la carte 68332.
Ces outils ayant évolué au cours du développement, ce document retrace aussi l'histoire de ces outils.
Pour développer le code de la carte principale du robot Snooky, nous avons eu besoin d'outils de développement logiciels, dont la mise en place est présentée dans le document intitulé ``développer sur 68332 avec les outils GNU'' [1] dont nous allons présenter l'utillisation. Ceux-cis seuls n'étaient pas suffisant. Il nous a fallu développer des outils permettant de commander directement la carte principale et de diagnostiquer l'état du robot.
Cette section présente les différents outils permettant d'arriver à un programme fonctionnel et de le debugger.
L'écriture du code en C doit suivre les conseils données dans le support de cours sur le C embarqué [2].
Pour compiler un programme C pour la carte principale, on utilise un compilateur GCC spécialement compilé pour le 683321
On utilise aussi un Makefile qui nous permet de définir et d'automatiser le processus de compilation.
Dans ce Makefile, nous avons défini une règle de production, permettant d'obtenir un fichier objet au format coff : ``.o'' à partir d'un fichier source ``.c'' à l'aide de gcc.
%.o : %.c $(HEADERS) $(CC) -c $< $(CFLAGS)
Au début du Makefile, il faut définir le compilateur utilisé à l'aide de la variable CC et les options de compilation à l'aide de la variable CFLAGS. On a par la même occasion défini les commandes utilisées pour invoquer tous les outils.
CC = m68k-coff-gcc -m68332 LD = m68k-coff-ld AS = m68k-coff-as -m68332 AR = m68k-coff-ar OBJCOPY = m68k-coff-objcopy RANLIB = m68k-coff-ranlib RM = rm -f TAS = tas CFLAGS += -g -I./libESEO -Wall C2SFLAGS += -I./libESEO -Wall LDFLAGS = -L./libESEO
Nous avons aussi défini une règle de production permettant d'obtenir un fichier objet à partir d'un fichier source assembleur au format gas2.
%.o : %.s $(AS) -o $@ $<
Il nous faut ensuite obtenir, à partir des différents fichiers objets dont nous disposons. Ceci se fait à l'aide de l'éditeur de liens ld.
Nous allons créer deux exécutables différents :
# construit pour etre envoye a gcc snooky_brain.coff : ldscript crt0.o snooky_brain.o $(OBJECTS) $(LD) -T ldscript $(OBJECTS) snooky_brain.o -lgcc -lm -lc /usr/local/lib/gcc-lib/m68k-coff/3.0.2/mcpu32/libgcc.a -lESEO332 $(LDFLAGS) -osnooky_brain.coff # code a envoyer en rom rom.coff : ldscript_flash crt0.o snooky_brain.o $(OBJECTS) $(LD) -T ldscript_flash $(OBJECTS) snooky_brain.o -lgcc -lm -lc /usr/local/lib/gcc-lib/m68k-coff/3.0.2/mcpu32/libgcc.a -lESEO332 $(LDFLAGS) -o rom.coff # S-Record pour envoyer le code a partir d'un flasheur rom.srec : rom.coff $(OBJCOPY) -O srec -S rom.coff rom.srec # Image binaire du code rom.bin : rom.coff $(OBJCOPY) -O binary -S rom.coff rom.bin
Les fichiers utilisés par l'éditeur de liens lui permettent de placer les différentes sections du programme (.text, .data et .bss) aux bons endroits dans le fichier objet résultant. Il définit aussi l'emplacement de ces sections une fois que le programme sera lancé.
Nous donnons ici l'exemple de ldscript_flash, décrivant à ld comment constituer un code objet permettant ensuite de charger le code après l'avoir mis en flash.
STARTUP(crt0.o) OUTPUT_ARCH(m68k) OUTPUT(rom.srec) /* nom par defaut */ SEARCH_DIR(/usr/local/m68k-coff/lib/mcpu32); SEARCH_DIR(/usr/local/lib/gcc-lib/m68k-coff/3.0.2/mcpu32); /* * Point d'entree pour gdb */ ENTRY (gdb_startup) /* * Un symbole pour definir l'emplacement de la pile * Ce symbole est utilisee par la routine sbrk, * chargee d'agrandir le tas */ __s_stack = 0x7F800; /* * Emplacement de la table d'exception en RAM */ __s_ex_table = 0x40000; __e_ex_table = 0x40400; SECTIONS { /* * Section .text (code) * Elle est placee dans la Flash et y reste */ .text 0x00000000 : AT (0) { __s_text = . ; /* definition de symboles */ * (.text) CONSTRUCTORS __e_text = . ; /* utilises dans le code d'initialisation*/ } > rom /* * Section .data * Placee juste apres .text dans la FLASH * pour etre copiee apres la table d'exception dans la RAM * lors de l'initialisation @0x20400 */ .data __e_ex_table : AT (SIZEOF(.text)) { __s_data = . ; /* Ces symboles permettent de savoir */ *(.data) __e_data = . ; /* ou copier .data dans la RAM */ } > ram /* * Section .bss (variables globales non initialisee) * Elle est placee dans la ram, juste apres .data */ .bss __e_ex_table + SIZEOF(.data) : { __s_bss = . ; /* Ces symboles permettent */ *(.bss) *(COMMON) __e_bss = . ; /* d'effacer le bss */ } > ram } /* * Definit la configuration memoire * Permet a ld de faire quelques petites verifications */ MEMORY { rom : ORIGIN = 0x00000000, LENGTH = 256K ram : ORIGIN = 0x00040000, LENGTH = 256k }
On remarque que pour chaque section, on bien défini l'adresse à laquelle elle sera placée pendant l'exécution du programme et l'adresse qu'il occupera en flash (spécifiée à l'aide de AT).
Pour la section .bss on n'a pas défini d'emplacement en ROM puisque .bss est la section où sont placées par gcc toutes les variables globales et statiques non initialisées (prenant par défaut la valeur 0). Cette section n'a pas de place dans le code objet puisqu'elle prendrait de la place inutile. Elle doit par contre être initialisée au démarrage du programme par le code de démarrage (crt0.s) qui a donc besoin des symboles __s_bss et __e_bss.
Le débugger, gdb à été utilisé non seulement en tant que debugger, mais aussi pour charger le programme directement en RAM.
Charger le programme directement en RAM à travers le BDM4 prends beaucoup moins de temps que de le flasher. Cela permet en plus une fois le code chargé de le debugger.
La documentation de Pavel Pisa sur le BDM [3] est très complète et donne un maximum d'informations au sujet de l'utilisation du BDM sous linux.
Pour fonctionner correctement, les outils utilisant le BDM5 utilisent le fichier cpu32init.
# on eteinds le watchdog W 0xFFFA21 0x0 1 # on ajuste la vitesse du processeur W 0xFFFA04 0x7F00 2 # initialisation des chip-selects W 0xFFFA44 0x00FF0000 4 W 0xFFFA48 0x00057870 4 W 0xFFFA4C 0x04055830 4 W 0xFFFA50 0x04053830 4
Ce fichier permet d'initialiser certains registres du 68332. Il sert par exemple à initialiser les Chip-Selects. En effet, si les Chip-Selects ne sont pas initialisés, il n'est pas possible d'accéder à la mémoire.
La syntaxe est très simple et est décrite dans [3]. Ici, par exemple, la commande W demande une écriture à une adresse mémoire (premier paramètre) de la valeur passée comme deuxième paramètre. Le troisième paramètre quant-à-lui spécifie la taille du transfert.
L'initialisation du BDM est très simple et systématique. Elle a donc été placée dans un script pour gdb, appellé icd.gdb6.
target bdm /dev/icd_bdm0 bdm_setdelay 0 bdm_reset bdm_autoreset off
La ligne de commande permettant de lancer gdb pour debugger la cible snooky_brain.coff sera donc la suivante :
m68k-bdm-coff-gdb -x icd.gdb snooky_brain.coff
Le chargement puis le lancement du programme se fait ensuite à l'aide de la commande run. Gdb va alors vous demander si vous voulez charger le programme en mémoire, il vous suffit à ce moment de répondre par l'affirmative pour que le programme soit effectivement chargé en mémoire puis exécuté.
Le programme bdm-load permet de contrôler le bdm. parmi ses fonctions, on en trouve une qui lui permet de flasher directement des flash.
Les flash utilisées dans le cadre de la carte principale du robot Snooky étaient deux AMD AM29F010 configurées pour utiliser un bus 16bits. Nous avions de la chance puisque bdm_load supporte effectivement cette configuration.
Bdm-load, tout comme gdb, utilise le fichier cpu32init pour initialiser le 68332 avant de procéder à l'envoi du code.
Pour faciliter l'envoi du code vers la cible, nous avons écrit une règle dans le Makefile lançant directement la commande de chargement. Cette règle est la règle load.
load : rom.srec bdm-load -f auto@csboot -c -r -e -s -l rom.srec
Expliquons les différents commutateurs utilisés :
Une documentation des actions effectuées par bdm-load peut être obtenue en utilisant le commutateur -h. De plus le document de Pavel Pisa [3] décrit assez bien comment utiliser bdm-load.
Le debuggage du code de la carte s'effectue à l'aide de gdb ou de son interface graphique ddd. Ddd est principalement utilisé pour les opérations plus lourdes.
Pour lancer ddd en utilisant la version de gdb compilée pour utiliser le bdm on utilise la ligne de commande suivante :
ddd --debugger m68k-bdm-coff-gdb -x icd.gdb snooky-brain.coff
Le premier organe que nous avons essayé de faire fonctionner sur la carte principale a été le port série.
En effet, l'interface série est un périphérique interne au 68332, ne demandant que très peu de connaissances pour être mis en oeuvre. De plus, cette interface série, nous l'avions déjà utilisée lors des miniprojets microprocesseurs en I1. Il était donc normal d'essayer d'écrire du code capable de nous ``parler'' au travers de cette liaison.
Le développement du code pour la carte principale étant réalisé sous Linux, nous avons choisi d'utiliser le programme minicom, équivalent à l'hyperterminal de Windows.
Les premiers programmes se limitaient à envoyer des données à minicom au travers de la liaison série, d'abord de simple caractère, des chaînes de caractères !!! Ces chaînes correspondaient à des résultats de traitements que l'on faisait éxécuter sur la carte.
Très vite on à ajouté à cette possibilité de visualisation de l'état de la carte la possibilité d'envoyer des ordres à partir du terminal. Ces ordres correspondaient en fait à l'envoi d'un caractère à l'aide du clavier. Le code de la carte principale s'occupait en fait à l'aide d'un gros switch, d'interpréter les ordres et de les exécuter.
C'est bien beau tout ca mais quand on debranche le PC, après on voit plus rien. Il était nécessaire de disposer d'une carte permettant de visualiser l'état du robot et de paramétrer son fonctionnment. Et oui, c'est la naissance de la carte de commande de la carte de commande, la fameuse carte sur laquelle on branche la tirette :).
Sur cette carte, nous avons disposé quatre leds, un bornier dip, et une prise pour brancher la tirette.
Quand il fallut tester les déplacements du robot, nous avons mis en place un séquenceur. Nous disposions d'un gros tableau ``en dur'', dans le code principal, contenant des séquences de commandes avec leurs paramètres. Les différentes séquences pouvaient être séléctionnées à l'aide du bornier et le lancement de la séquence s'effectuait soit à l'aide de la tirette (qui n'était même pas une vraie tirette à l'époque), soit à l'aide d'une commande sur le terminal.
Les commandes du séquenceur étaient constituées non seulement de commandes de déplacement du robot mais aussi de commandes de visualisation à l'aide des quatre leds. Ainsi il nous était possible de savoir où en était le robot dans sa séquence. Une fois que la méca à été prête, nous avons ajouté des commandes de gestion de la carte mécanique. Le robot fonctionnait en autonome.
Le code de ce séquenceur n'est plus compilé avec le reste du code. Les fichiers source correspondant sont par contre toujours là, dans les fichier sequenceur.h et sequenceur.c.
Lorsqu'il fallut songer à implémenter le code gérant la caméra, l'interface avec la carte principale n'était pas encore prête.
La caméra utilisant une liaison série pour dialoguer avec la carte principale, nous avons décidé d'écrire un programme simulant cette caméra sur PC.
Le problème posé par cette liaison est qu'elle utilise la liaison série de la carte principale, déjà utilisée pour le débuggage.
Le programme simu_cam, qui se limitait à envoyer une information concernant la position des balles à la carte principale s'est donc vu adjoindre une console, remplaçant le programme minicom, jusqu'alors utilisé pour remplir cette fonction.
Nous y avons aussi ajouté des boutons permettant d'invoquer rapidement des commandes de la carte principale.
La figure 1 présente la fenêtre principale du programme simu_cam. Celle-ci est composée de deux console, l'une affichant des messages interprétés par le programme, l'autre, ceux que celui-ci n'a pas pu interpréter. La fenêtre principale possède aussi une barre de boutons située sur la gauche de l'écran.
Pour faciliter le debuggage, nous avons ajouté un terrain virtuel dans une fenêtre séparée.
Cette fenêtre, visible sur la figure 2 permet de placer des balles pour tester l'envoi des coordonnées de celles-ci dans la carte principale. Le placement se fait soit manuellement, soit de manière aléatoire. Au cours des développement, nous avons ajouté la possibilité de visualiser la position de notre robot et du robot adverse sur le terrain virtuel.
Dernière amélioration apportée au processus de test. Pour faciliter ceux-ci, nous avons ajouté au programme de la carte principale la possibilité d'envoyer des informations de debuggage vers le PC. Ces informations peuvent être envoyées pendant l'interruption timer. Grâce au bornier, on peut choisir d'empêcher l'envoi de ces informations pendant l'interruption, ce qui évite de polluer la communication avec la caméra (quand on mets la vraie caméra).
La figure 3 est une capture d'écran du panneau affichant les informations en provenance de la carte principale.