jeudi 9 janvier 2014

se faire les dents sur XMOS startKIT

Il y a quelques semaines je présentais le startKIT de XMOS. Depuis j'ai lu pas mal de documentation et cette semaine je me suis fait les dents sur le startKIT en écrivant ma première application. Ça n'a pas été facile de m'adapter aux contraintes du langage XC mais avec de la persévérance j'y suis finalement arrivé.

le jeux de la vie

Le jeux de la vie est un automate cellulaire sur 2 dimensions imaginé par le mathématicien John H. Conway en 1970. Soit une grille ou chaque carré (cellule) peut prendre 2 couleurs, disons noir ou blanc. A chacune des cellules de la grille on applique un ensemble de règles simples et on conserve le nouvel état de la grille et recommence la même opération à partir de ce nouvel état. Lorsque Conway a imaginé cet automate les ordinateurs personnels n'existaient pas encore, chaque itération était calculé à la main et dessiné sur du papier quadrillé. Heureusement on n'en est plus là. C'est beaucoup plus intéressant de confier ce travail à un ordinateur et d'afficher le résultat de chaque itération à l'écran.

J'ai choisi le jeux de la vie pour me faire les dents sur le startKIT car c'est une programmation simple, du moins lorsque c'est écris en 'C' en single thread. Lorsqu'on doit apprendre une nouvelle façon de faire c'est plus difficile comme je l'ai constaté.

La grille est toroïdale et de dimension 36x26. Par toroïdale on entends ceci. Imaginez que l'écran de l'ordinateur est une feuille de papier et qu'on le pli en cylindre pour que le haut vienne touché au bas et qu'ensuite on étire ce cylindre pour que le 2 bouts se touchent. On obtient ainsi une tore et les cellules sont à la surface de cette tore. L'univers ainsi créé est sans rebords mais de surface limité. Ainsi lorsqu'un glider se déplace sur la grille, au lieu de disparaître lorsqu'il arrive à un rebord de l'écran il réapparait de l'autre côté.

Voici un vidéo de la simulation en action. Sur la ligne du bas s'affiche le nombre d'itérations.

Le calcul de chaque itération étant trop rapide j'ai ajouter une délais 0,5 seconde entre chaque itération.

schématique

Code source

Description

Le tableau video_buffer est la mémoire vidéo qui contient l'information à afficher à l'écran. Cette mémoire contient les indices des caractères dans la table font. life_buffer contient l'état de l'univers de l'automate cellulaire. Ce tableau est double car lors du calcul d'une itération la version originale ne peut-être modifiée. On écris donc le résultat dans l'autre moitié et on alterne entre les deux moitiés à chaque itération.

Dans la procédure principale on initialise la grille life_buffer en utilisant le générateur pseudo-hasard rand(). La variable timer t n'est utilisée que pour initialiser le générateur pseudo-hasard.

Le générateur de signal NTSC utilise 3 ports de 1 bit, ntsc_sync_port génère le signal de synchronisation. ntsc_blvl_port détermine le niveau noir et finalement ntsc_video_port est la sortie des pixels vidéo.

interface

Dans le langage XC une interface permet de définir un canal de communication entre 2 threads. Dans ce programme il y a 2 threads next_gen et ntsc_video. Chacun de ces threads s'exécute sur un core différent. Comme la variable video_buffer et contrôlée par le thread ntsc_video le thread next_gen ne peut y accéder. Donc lorsque next_gen a terminé le calcul d'une itération il envoie le nouveau tableau au thread ntsc_video par l'interface cells_array. Cette interface ne contient qu'une seule fonction CopyCellArray(). le nouveau tableau est copié dans video_buffer pendant le vertical retrace donc il n'y a pas de glitches dans l'affichage.

Dans la fonction ntsc_video() il y a un select. Les select en XC servent à écouter un canal de communication en attente d'information. Pour chaque fonction de l'interface il doit y avoir un case correspondant dans le select. Chaque case doit-être terminé par un break ou un return. Pour éviter que le thread ne bloque sur l'attente du message fournis par CopyCellArray() on utilise un

default:break;
. Le default permet de sortie du select et continuer l'exécution du thread. S'il n'y avait pas de cette option le générateur NTSC ne fonctionnerais pas.

Une interface est un canal client/serveur. C'est à dire qu'un des thread est le serveur c'est à dire celui qui attends les requêtes distribuées via les fonctions de l'interface. Le client lui évidemment envoie les requêtes.

Dans la fonction main(),

interface cells_array ca;
déclare une variable d'interface ca qui est passé en arguments aux 2 threads qui utilisent cette interface. Notez comment le paramètre d'interface est déclaré dans chaque thread. client pour next_gen() et server pour ntsc_video().
void next_gen(client interface cells_array ca) 
void ntsc_video(out port sync_port,
                out port blvl_port,
                out port video_port,
                server interface cells_array cells) 
Les interfaces sont des canaux de communications point à point donc la variable ca ne peut-être passée en argument qu'à un seul thread client et un seul thread server. Si un serveur a plusieurs clients il faut déclarer une variable différente pour chaque client. l'ensemble de ces variables peut-être gardé dans un tableau qui est passé en argument au serveur.

Conclusion

Bien que de se familiariser avec une nouvelle technologie demande des efforts le succès est toujours gratifiant. La version 0.2 de ce programme pourrait utiliser les 2 règles tactiles et le bouton qui sont sur la carte startKIT pour créer une interface utilisateur servant à initialiser la grille au lieu de le faire avec une fonction au hasard. A suivre.


Mise à jour 2014-01-18

game of life version 0.2

  • Initialisation manuelle de la grille en utilisant le bouton, slider x, slider y qui sont sur la carte startKIT.
  • augmentation du nombre de cellules, grille de 176x110.

Au démarrage le mot INITIALISATION apparaît au bas de l'écran et un pixel clignote au centre (curseur). En utilisant les 2 slider on déplace le curseur sur la grille. A l'endroit désiré on presse le bouton pour mettre une cellule à cette endroit. S'il y a déjà une cellule elle est effacée (inverse l'état sous le curseur). Pour sortir de l'initialisation il faut garder le bouton enfoncé 2 secondes.

Dans ce vidéo la configuration initiale est un U constitué de 7 cellules comme indiqué sur la page jeux_de_la_vie sur wikipekdia. On aperçoit donc la face de clown à l'itération 110.

Code source

`

J'ai réorganisé le code source en plusieurs fichiers, de plus ce programme utilise 2 modules fournis par XMOS, module_startkit_gpio et module_capacitive_sensing

Le projet peut-être téléchargé ici.

Difficulté avec l'IDE

Lorsqu'on utilise des modules dans un projet il ne suffit pas de les référencés dans les propriétés du projet comme ici.

Ça ne suffit pas pour que le compilateur/lieur trouve le code source. Il faut indiquer les modules dans le Makefile.

Dans le project explorer on double-clique sur le Makefile pour ouvrir une fenêtre de configuration du Makefile.

Dans le groupe Used Modules on coche les modules utilisés par le projet.

J'ai du chercher avant de trouver la solution. Si on référence un module dans les propriétés du projet il me semble que l'IDE devrait configuré le Makefile en conséquence.


Si les automates cellulaires vous intéressent Golly est un excellent logiciel de simulation multiplateforme. Il ne simule pas seulement le jeux de la vie mais de nombreux autres types d'automates 1D ou 2D. Vous pouvez-même créer vos propres règles. Il y a de nombreux exemples de configurations qui donnent des animations intéressantes.

Aucun commentaire:

Enregistrer un commentaire