| 1 | {{{ |
| 2 | #!html |
| 3 | <h1>TP1: Description d'application avec DSX, et exécution sur station de travail</h1> |
| 4 | }}} |
| 5 | [[PageOutline]] |
| 6 | |
| 7 | |
| 8 | = 0. Objectif = |
| 9 | |
| 10 | L'objectif de ce premier TP est de vous familiariser avec le langage de description DSX |
| 11 | (comme Design Space Explorer). Ce langage permet au concepteur de déployer une application |
| 12 | logicielle multi-tâches (écrite en C) sur une architecture matérielle multi-processeurs (MP-SoC), |
| 13 | modélisée avec les composants matériels fournis par la bibliothèque SoCLib. |
| 14 | |
| 15 | Le langage de description DSX est une API implémentée à l'aide du langage Python, et il permet au concepteur |
| 16 | de faire 3 choses: |
| 17 | * Définir la structure de l'application logicielle multi-tâches, c'est à dire le Graphe des Tâches et des Communications. |
| 18 | (aussi appelé TCG: Tasks&Communication Graph). |
| 19 | On suppose que le parallélisme "gros grain" de l'application et le shéma des communication entre les |
| 20 | tâches peuvent être statiquement définis par le concepteur, et n'évoluent pas en cours d'exécution. |
| 21 | * Définir l'architecture matérielle, c'est à dire définir le nombre de processeurs, le nombre de bancs mémoires, |
| 22 | la taille des caches, le type d'inerconnect utilisé, etc... |
| 23 | * Contrôler le déploiement de l'application logicielle sur la plate-forme matérielle, c'est à dire le placement |
| 24 | des tâches sur les processeurs et le placement des canaux de communication sur les bancs mémoire. |
| 25 | |
| 26 | L'exécution de cette description DSX permet générer trois choses: |
| 27 | * Une version de l'application logicielle multi-tâches compatible POSIX, qui peut être |
| 28 | compilée et exécutée sur n'importe quelle station de travail supportant l'API des threads POSIX. |
| 29 | Cette première version permet de valider fonctionnellement l'application logicielle, indépendamment |
| 30 | de toute architecture MP-SoC. |
| 31 | * Un ensemble de fichiers de directives permettant de compiler l'application logicielle pour le(s) |
| 32 | processeur(s) embarqué(s) sur le MP-SoC, d'effectuer l'édition de liens avec le système d'exploitation |
| 33 | embarqué, et de générer le code binaire exécutable. |
| 34 | * Un modèle SystemC complet de l'architecture matérielle, correctement configuré pour respecter |
| 35 | l'organisation de l'espace adressable défini par le concepteur, permettant de générer un simulateur |
| 36 | complet de cette architecture, capable d'exécuter en simulation le code embarqué. |
| 37 | |
| 38 | Dans ce 1^er^ TP, on se limitera à décrire - en langage DSX - la structure de l'application logicielle MJPEG, |
| 39 | à écrire quelques unes des tâches de l'application MJPEG, et à valider cette application en l'exécutant sur |
| 40 | une station de travail GNU/Linux. |
| 41 | |
| 42 | Vous fournirez un rapport rédigé, en format Adobe Acrobat (PDF), ainsi que certaines sources. Tous les détails sont |
| 43 | à la fin dans la section 'Compte-Rendu'. Les points du présent sujet devant faire l'objet d'un écho dans votre rapport sont |
| 44 | en '''''gras oblique''''', préfixés de [[Image(MjpegCourse:q.gif)]]. |
| 45 | |
| 46 | = 1. Prise en main = |
| 47 | |
| 48 | == 1.1. Exécuter l'application SplitMsg == |
| 49 | |
| 50 | Pour prendre en main l'outil DSX, on s'intéresse à une application parallèle |
| 51 | extrêmement simple comportant deux tâches et un seul canal de communication MWMR. |
| 52 | Cette application s'appelle !SplitMsg. |
| 53 | * Importez l'environnement nécessaire dans le contexte de votre ''shell'' |
| 54 | {{{ |
| 55 | $ source /users/outil/dsx/dsx_env.sh |
| 56 | }}} |
| 57 | * Créez un répertoire `SplitMsg` dans lequel vous mettrez les fichiers à recopier. |
| 58 | * Pour les fichiers, voir la page SplitMsg |
| 59 | * Si ce n'est pas déjà fait, rendez la description DSX exécutable |
| 60 | {{{ |
| 61 | $ chmod +x fichier_de_description |
| 62 | }}} |
| 63 | * Exécutez la description DSX |
| 64 | {{{ |
| 65 | $ ./fichier_de_description |
| 66 | }}} |
| 67 | * [[Image(MjpegCourse:q.gif)]] Q1: '''''Quels fichiers ou répertoires ont été créés?''''' |
| 68 | * Lancez la compilation l'application logicielle générée par DSX, en utilisant le makefile également généré |
| 69 | par DSX. |
| 70 | {{{ |
| 71 | $ make |
| 72 | }}} |
| 73 | * Lancez le programme multitâche généré qui porte le nom "exe.posix". |
| 74 | Vous pourrez interrompre l'exécution à tout moment en pressant Ctrl-c. |
| 75 | {{{ |
| 76 | $ ./exe.posix |
| 77 | }}} |
| 78 | * [[Image(MjpegCourse:q.gif)]] Q2: '''''Comment interprêter ce que vous observez lors de l'exécution de cette application ?''''' |
| 79 | |
| 80 | == 1.2. Anatomie de la description DSX == |
| 81 | |
| 82 | Dans DSX, on fait une distinction entre un modèle de tâche et une instance de tâche, |
| 83 | car un même modèle de tâche peut être instancié plusieurs fois dans une application. |
| 84 | * Un modèle de tâche est défini par la directive `TaskModel`. Il spécifie |
| 85 | pour une tâche ses ressources utilisées (canaux de communication, ...) |
| 86 | ainsi que ses implémentations existantes. Pour l'instant, on ne s'intéressera |
| 87 | qu'aux implémentations logicielles déclarées par `SwTask`. voir DsxTaskModel |
| 88 | * Une instance de tâche est définie par la directive `Task`. Elle fait partie d'un |
| 89 | TCG. Elle est connectée aux autres tâches par les ressources. voir DsxTasks |
| 90 | |
| 91 | Dans les applications décrites dans ce TP, chaque modèle de tâche ne sera utilisé qu'une fois. |
| 92 | |
| 93 | La description DSX de l'application SplitMsg est en trois parties. |
| 94 | |
| 95 | [[Image(MjpegCourse:q.gif)]] Q3: '''''A quoi sert chacune des parties ?''''' |
| 96 | |
| 97 | = 2. Application MJPEG = |
| 98 | |
| 99 | Dans tout le reste du TP, on s'intéressera à l'application MJpeg telle que décrite en cours. |
| 100 | On en rappelle le graphe de tâches: |
| 101 | |
| 102 | [[Image(MjpegCourse:mjpeg.png)]] |
| 103 | |
| 104 | Chaque tâche effectue un traitement élémentaire dans la décompression d'une image. |
| 105 | Dans ce TCG, on représente les tâches par des ronds et les canaux de communication |
| 106 | par des rectangles. Il s'agit donc d'un graphe bipartie. |
| 107 | |
| 108 | Notre animation MJPEG sera composée d'images faisant toutes la même taille. |
| 109 | Comme une compression JPEG découpe l'image en blocs de 8x8 pixels, chacune des |
| 110 | dimensions de l'image sera multiple de 8 (on ne gère qu'un nombre entier de blocs). |
| 111 | Nous allons utiliser les constantes suivantes: |
| 112 | * WIDTH largeur de l'image en pixels |
| 113 | * HEIGHT hauteur de l'image en pixels |
| 114 | A partir de ces deux constantes, d'autres constantes sont définies dans le fichier `jpeg.h`: |
| 115 | * BLOCKS_W nombre de blocs en largeur |
| 116 | * BLOCKS_H nombre de blocs en hauteur |
| 117 | * NBLOCKS nombre de blocs par image (= BLOCKS_W*BLOCKS_H) |
| 118 | |
| 119 | == 2.1. Spécifier le TCG == |
| 120 | |
| 121 | Il faut nommer chaque élément du TCG: |
| 122 | * Les noms des tâches ont été définis en cours et sont imposés. |
| 123 | * Vous pouvez choisir librement les noms des canaux de communication. |
| 124 | Chaque canal de communication est attaché à au moins deux tâches par des ''ports''. |
| 125 | On distingue le nom des canaux de communication et le nom des ports des tâches connectées à ces canaux. |
| 126 | |
| 127 | Pour déterminer les noms des ports des tâches, il est impératif de consulter |
| 128 | le code des tâches fourni dans le fichier attachment:mjpeg_tp1.tar.bz2, |
| 129 | recopiez ce fichier chez vous et décompressez-le. |
| 130 | {{{ |
| 131 | $ tar xjvf mjpeg_tp1.tar.bz2 |
| 132 | }}} |
| 133 | '''Note''': Même si le code sources des tâches {{{iqzz}}} et {{{libu}}} |
| 134 | n'est pas fourni, vous pouvez connaître les largeurs de |
| 135 | tous les canaux en vous référant au code des autres tâches, et le nom des |
| 136 | ports est fourni dans la description DSX, dans le fichier {{{mjpeg.py}}} |
| 137 | (à compléter). |
| 138 | |
| 139 | Dans le fichier de description DSX {{{mjpeg.py}}}, |
| 140 | * Pour chacun des modèles de tâches: |
| 141 | * Reportez des noms pour chacun des ports d'entrée/sortie (cf DsxTasks). |
| 142 | * Complétez la description des modèles de tâches |
| 143 | Iqzz, et Libu ont une déclaration particulière à ne pas prendre en compte pour l'instant, |
| 144 | car ces modèles tâches sont fournis sans les sources: vous les écrirez aux prochaines questions |
| 145 | * Pour chaque canal de communication: |
| 146 | * Choisissez un nom et instanciez le canal |
| 147 | (Nous avons 8 fifos Mwmr, utiliser l'API décrite dans DsxResource, `tg_demux` est fournie en exemple) |
| 148 | * Dimensionnez (profondeur et largeur) les canaux en fonction des contraintes imposées par le code des tâches. |
| 149 | * Créez un Tcg |
| 150 | * en instanciant une tâche de chaque modèle, voir DsxTcg |
| 151 | * en connectant les canaux aux ports des tâches, en les désignant par leurs noms |
| 152 | |
| 153 | == 2.2. Exécution de l'application == |
| 154 | |
| 155 | * Exécutez la description |
| 156 | {{{ |
| 157 | $ ./mjpeg.py |
| 158 | }}} |
| 159 | * Lancez la compilation de l'application |
| 160 | {{{ |
| 161 | $ make |
| 162 | }}} |
| 163 | * Lancez l'exécution de l'application |
| 164 | {{{ |
| 165 | $ ./exe.posix |
| 166 | }}} |
| 167 | * [[Image(MjpegCourse:q.gif)]] Q4: '''''Décrivez brièvement ce que vous observez''''' |
| 168 | |
| 169 | == 2.3. Écriture en C de la tâche IQZZ == |
| 170 | |
| 171 | IQZZ est une tâche faisant un double traitement, appliqué successivement à chaque bloc de 8x8 pixels de l'image. |
| 172 | |
| 173 | IQZZ nécessite un tableau de quantisation inverse T, venant de la tâche ''Demux'' par un canal de communication dédié. |
| 174 | |
| 175 | Cette table doit être lue '''une fois par image''', elle sert au traitement de '''tous''' les blocs |
| 176 | d'une image. Le nombre de blocs dans l'image est donné par la constante NBLOCKS, définie dans "jpeg.h". |
| 177 | |
| 178 | Un bloc entrant dans IQZZ est composé de 8x8=64 facteurs. |
| 179 | ||F,,0,,||F,,1,,||F,,2,,||F,,3,,||F,,4,,||F,,5,,||F,,6,,||F,,7,,|| |
| 180 | ||F,,8,,||F,,9,,||F,,10,,||F,,11,,||F,,12,,||F,,13,,||F,,14,,||F,,15,,|| |
| 181 | ||F,,16,,||F,,17,,||F,,18,,||F,,19,,||F,,20,,||F,,21,,||F,,22,,||F,,23,,|| |
| 182 | ||F,,24,,||F,,25,,||F,,26,,||F,,27,,||F,,28,,||F,,29,,||F,,30,,||F,,31,,|| |
| 183 | ||F,,32,,||F,,33,,||F,,34,,||F,,35,,||F,,36,,||F,,37,,||F,,38,,||F,,39,,|| |
| 184 | ||F,,40,,||F,,41,,||F,,42,,||F,,43,,||F,,44,,||F,,45,,||F,,46,,||F,,47,,|| |
| 185 | ||F,,48,,||F,,49,,||F,,50,,||F,,51,,||F,,52,,||F,,53,,||F,,54,,||F,,55,,|| |
| 186 | ||F,,56,,||F,,57,,||F,,58,,||F,,59,,||F,,60,,||F,,61,,||F,,62,,||F,,63,,|| |
| 187 | |
| 188 | On applique sur ce bloc deux traitement successifs: |
| 189 | |
| 190 | * La quantisation inverse (IQ) est la multiplication de chaque élément d'entrée par un facteur |
| 191 | de la table de 64 coefficients de quantisation inverse T,,n,,, globale pour l'image. |
| 192 | |
| 193 | F,,n,,' = F,,n,, * T,,n,, |
| 194 | |
| 195 | ||F,,0,,'||F,,1,,'||F,,2,,'||F,,3,,'||F,,4,,'||F,,5,,'||F,,6,,'||F,,7,,'|| |
| 196 | ||F,,8,,'||F,,9,,'||F,,10,,'||F,,11,,'||F,,12,,'||F,,13,,'||F,,14,,'||F,,15,,'|| |
| 197 | ||F,,16,,'||F,,17,,'||F,,18,,'||F,,19,,'||F,,20,,'||F,,21,,'||F,,22,,'||F,,23,,'|| |
| 198 | ||F,,24,,'||F,,25,,'||F,,26,,'||F,,27,,'||F,,28,,'||F,,29,,'||F,,30,,'||F,,31,,'|| |
| 199 | ||F,,32,,'||F,,33,,'||F,,34,,'||F,,35,,'||F,,36,,'||F,,37,,'||F,,38,,'||F,,39,,'|| |
| 200 | ||F,,40,,'||F,,41,,'||F,,42,,'||F,,43,,'||F,,44,,'||F,,45,,'||F,,46,,'||F,,47,,'|| |
| 201 | ||F,,48,,'||F,,49,,'||F,,50,,'||F,,51,,'||F,,52,,'||F,,53,,'||F,,54,,'||F,,55,,'|| |
| 202 | ||F,,56,,'||F,,57,,'||F,,58,,'||F,,59,,'||F,,60,,'||F,,61,,'||F,,62,,'||F,,63,,'|| |
| 203 | |
| 204 | * Le !ZigZag (ZZ) est un réordonnancement des pixels d'un bloc en diagonale. Il permet d'améliorer la compression. |
| 205 | |
| 206 | Après le réordonnancement, l'ordre des facteurs en sortie doit être: |
| 207 | |
| 208 | ||F,,0,,'||F,,1,,'||F,,5,,'||F,,6,,'||F,,14,,'||F,,15,,'||F,,27,,'||F,,28,,'|| |
| 209 | ||F,,2,,'||F,,4,,'||F,,7,,'||F,,13,,'||F,,16,,'||F,,26,,'||F,,29,,'||F,,42,,'|| |
| 210 | ||F,,3,,'||F,,8,,'||F,,12,,'||F,,17,,'||F,,25,,'||F,,30,,'||F,,41,,'||F,,43,,'|| |
| 211 | ||F,,9,,'||F,,11,,'||F,,18,,'||F,,24,,'||F,,31,,'||F,,40,,'||F,,44,,'||F,,53,,'|| |
| 212 | ||F,,10,,'||F,,19,,'||F,,23,,'||F,,32,,'||F,,39,,'||F,,45,,'||F,,52,,'||F,,54,,'|| |
| 213 | ||F,,20,,'||F,,22,,'||F,,33,,'||F,,38,,'||F,,46,,'||F,,51,,'||F,,55,,'||F,,60,,'|| |
| 214 | ||F,,21,,'||F,,34,,'||F,,37,,'||F,,47,,'||F,,50,,'||F,,56,,'||F,,59,,'||F,,61,,'|| |
| 215 | ||F,,35,,'||F,,36,,'||F,,48,,'||F,,49,,'||F,,57,,'||F,,58,,'||F,,62,,'||F,,63,,'|| |
| 216 | |
| 217 | Notes d'implémentation: |
| 218 | * Pour implémenter ZZ, un tableau statique commençant par les |
| 219 | valeurs ZZ![0]=0, ZZ![1]=1, ZZ![2]=8, ZZ![3]=16, ZZ![4]=9, ... |
| 220 | vous sera probablement utile. |
| 221 | * Les transformations IQ et ZZ doivent être implémentées dans |
| 222 | la même boucle. |
| 223 | * Les types des données sont: |
| 224 | * T: Table de quantisation inverse (IQ): entiers non signés 8 bits |
| 225 | * F,,n,,: Blocs en entrée: entiers 16 bits signés |
| 226 | * F,,n,,': Blocs en sortie: entiers 32 bits signés (car 8bits*16bits nécessite au plus 24 bits...) |
| 227 | * Votre code '''doit''' être portable quelle que soit l'endianness du processeur sous-jacent |
| 228 | (si vous ne faites pas de transtypages hasardeux sur les pointeurs, ça devrait bien se passer) |
| 229 | * Votre code '''doit''' gérer toutes les tailles d'images (tant qu'elles sont multiples de 8x8). |
| 230 | Toutes les boucles doivent utiliser les tailles issues des defines (WIDTH, HEIGHT, BLOCKS_W, BLOCKS_H) |
| 231 | |
| 232 | Instructions: |
| 233 | * Écrivez en C le code de la tâche IQZZ à l'aide de l'API logicielle définie dans SrlApi |
| 234 | * Réécrivez la définition de la tâche IQZZ dans la description DSX |
| 235 | {{{ |
| 236 | # On avait: |
| 237 | LlvmBlob('iqzz', stack_size = 1024, blob = 'src/iqzz_48x48.bc') |
| 238 | |
| 239 | # On peut alors déclarer iqzz comme une tâche logicielle en C. |
| 240 | SwTask( ... ) |
| 241 | }}} |
| 242 | Inspirez-vous des autres déclarations, n'oubliez pas les `defines` si vous voulez un code portable. |
| 243 | |
| 244 | En fonction de la définition d'{{{iqzz}}} que vous utilisez (celle de `LlvmBlob` ou la vôtre), |
| 245 | et en recompilant, vous observerez les résultats l'implémentation de référence ou de la vôtre. |
| 246 | |
| 247 | * Affinez votre fonction. Si besoin, lancez l'application {{{exe.posix}}} dans un débugger. |
| 248 | La fonction implémentant {{{iqzz}}} portera probablement le nom {{{iqzz_func_iqzz}}}. |
| 249 | |
| 250 | == 2.4. Écriture en C de la tâche LIBU == |
| 251 | |
| 252 | Un Ramdac est une RAM couplée à un DAC (Digital to Analog Converter). Le contenu de la ram est |
| 253 | converti en signal analogique pour être envoyé sur un écran. Notre Ramdac a un accès particulier: |
| 254 | Il a un comportement Fifo. Il faut écrire les pixels dans l'ordre où ils vont être affichés : |
| 255 | tous les pixels d'une ligne, puis toutes les lignes d'une image. |
| 256 | |
| 257 | Il se trouve que les blocs issus de la décompression JPEG font 8x8 pixels. Ils ne font pas la |
| 258 | largeur de l'image, il faut donc construire des lignes d'image à partir des blocs issus de la décompression. |
| 259 | C'est le but de la tâche Libu (Line Builder). |
| 260 | |
| 261 | Libu prend BLOCKS_W blocs de 8x8 pixels et en construit 8 lignes de WIDTH pixels de large |
| 262 | (rappel: BLOCKS_W*8 = WIDTH). Il peut alors envoyer successivement ces lignes au Ramdac. |
| 263 | |
| 264 | En pseudo-code, le traitement de Libu est: |
| 265 | {{{ |
| 266 | bloc : 8x8 pixels |
| 267 | buffer : WIDTH*8 pixels |
| 268 | |
| 269 | Pour chaque 0 .. BLOCKS_H: |
| 270 | Pour chaque 0 .. BLOCKS_W: |
| 271 | Lire un bloc |
| 272 | Pour chaque ligne du bloc |
| 273 | Copier les 8 pixels en les mettant à leur place dans buffer |
| 274 | Pour chacune des 8 lignes du buffer: |
| 275 | Envoyer la ligne |
| 276 | }}} |
| 277 | * Implémentez cette tâche en C à l'aide de l'API logicielle définie dans SrlApi |
| 278 | * Modifiez la description de l'application DSX pour prendre en compte votre source |
| 279 | * Testez l'application nouvellement compilée |
| 280 | |
| 281 | = 3. Compte-Rendu = |
| 282 | |
| 283 | Vous devrez créer une archive `tar.gz`, contenant un seul répertoire nommé `tp1`. Dans ce répertoire vous devrez mettre: |
| 284 | * Un fichier `__init__.py` vide |
| 285 | * Un fichier `rendu.py` contenant: |
| 286 | {{{ |
| 287 | |
| 288 | from dsx import * |
| 289 | |
| 290 | iqzz = TaskModel( # le reste de la définition de votre modèle iqzz |
| 291 | ) |
| 292 | libu = TaskModel( # le reste de la définition de votre modèle libu |
| 293 | ) |
| 294 | }}} |
| 295 | Et rien d'autre, en particulier rien à propos des autres tâches ou du TCG. |
| 296 | * Le code des tâches iqzz et libu que vous avez écrites dans un sous-répertoire `src/`. |
| 297 | * Votre rapport (une page maximum) en format PDF (et aucun autre) dans `tp1/rapport.pdf`. |
| 298 | |
| 299 | Le nom de fichier de l'archive doit contenir les nom des deux auteurs, séparés par un ''underscore'' (_), |
| 300 | par exemple: `dupond_dupont.tar.gz`. |
| 301 | |
| 302 | Faites particulièrement attention à cette archive. elle fera l'objet d'une correction automatique pour la |
| 303 | validation des sources, d'où le format strict. |
| 304 | |
| 305 | Pour être surs de vous, le listing du contenu de l'archive doit donner cette liste avec ces noms, |
| 306 | et rien de plus (l'ordre des fichiers n'importe pas): |
| 307 | {{{ |
| 308 | $ tar tzf nombinome0_nombinome1.tar.gz |
| 309 | tp1/ |
| 310 | tp1/__init__.py |
| 311 | tp1/src/ |
| 312 | tp1/src/iqzz.c |
| 313 | tp1/src/libu.c |
| 314 | tp1/rapport.pdf |
| 315 | tp1/rendu.py |
| 316 | $ |
| 317 | }}} |
| 318 | |
| 319 | Envoyez cette archive avant le 13/02/2007, 18h00 à [MailAsim:nipo Nicolas Pouillon]. |