| | 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]. |