10 | | L'objectif de ce premier TP est de vous familiariser avec le langage de description DSX/L |
11 | | (comme Design Space Explorer/Language). 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/L 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 & Communications Graph). |
19 | | On suppose donc 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/L 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 modèle SystemC complet de l'architecture matérielle du MP-SoC, configuré pour respecter |
32 | | l'organisation de l'espace adressable définie par le concepteur. Ce modèle SystemC est compilé pour |
| 10 | L'objectif de ce premier TP est de vous familiariser avec le langage de description '''DSX/L''' (pour ''Design Space !Explorer/Language''). Ce langage permet à un concepteur de déployer une application logicielle multi-tâches (écrite en C) sur une architecture matérielle multiprocesseur (''MP-SoC''), modélisée avec les composants matériels fournis par la bibliothèque '''SoCLib'''. |
| 11 | |
| 12 | Le langage de description DSX/L est une API (interface de programmation) implémentée à l'aide du langage Python, qui permet à un concepteur la réalisation des 3 tâches suivantes : |
| 13 | |
| 14 | 1. Définir la structure de l'application logicielle multi-tâches, c'est à dire le ''Graphe des Tâches et des Communications'' (aussi appelé ''TCG'', pour ''Tasks and Communications Graph''). Par cette structure, on suppose que le parallélisme "gros grain" de l'application ainsi que le schéma des communication entre les |
| 15 | tâches peuvent être statiquement définis par le concepteur et n'évoluent pas en cours d'exécution. |
| 16 | 1. Définir l'architecture matérielle, c'est-à-dire définir le nombre de processeurs, le nombre de bancs mémoires, la taille des caches processeurs, le type de réseau d'interconnexion utilisé, etc. |
| 17 | 1. Contrôler le déploiement de l'application logicielle sur la plate-forme matérielle, c'est-à-dire le placement des tâches sur les processeurs et le placement des canaux de communication dans les bancs mémoire. |
| 18 | |
| 19 | L'exécution de cette description DSX/L permet générer trois éléments : |
| 20 | 1. Une version de l'application logicielle multi-tâches compatible POSIX, pouvant donc être compilée et exécutée sur n'importe quelle station de travail supportant l'API des threads POSIX. Cette première version permet de valider fonctionnellement l'application logicielle, indépendamment de toute architecture MP-SoC. |
| 21 | 1. Un modèle SystemC complet de l'architecture matérielle du MP-SoC, configuré pour respecter l'organisation de l'espace adressable définie par le concepteur. Ce modèle SystemC est compilé pour |
38 | | Dans ce 1^er^ TP, on se limitera à décrire - en langage DSX/L - 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 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)]]. |
| 25 | Dans ce 1^er^ TP, on se limitera à décrire - en langage DSX/L - la structure de l'application logicielle MJPEG, à écrire quelques unes des tâches de l'application MJPEG, et à valider cette application en l'exécutant sur une station de travail Linux. |
| 26 | |
| 27 | Vous fournirez un rapport rédigé, au format PDF, ainsi que certains fichiers de code source. Tous les détails sont expliqués à la fin de cette page, dans la section 'Compte-Rendu'. Les points du présent sujet devant faire l'objet d'un écho dans votre rapport sont signalés en '''''gras oblique''''' et préfixés de [[Image(MjpegCourse:q.gif)]]. |
80 | | On fait une distinction entre un modèle de tâche et une instance de tâche, |
81 | | car un même modèle de tâche peut être instancié plusieurs fois dans une application. |
82 | | * Un modèle de tâche est défini par la directive `TaskModel`. Il spécifie |
83 | | pour une tâche ses ressources utilisées (canaux de communication, ...) |
84 | | ainsi que ses implémentations existantes. Pour l'instant, on ne s'intéressera |
85 | | qu'aux implémentations logicielles déclarées par `SwTask`. voir DsxTaskModel |
86 | | * Une instance de tâche est définie par la directive `Task`. Elle fait partie d'un |
87 | | TCG. Elle est connectée aux autres tâches par les ressources. voir DsxTasks |
| 61 | DSX fait une distinction entre un ''modèle de tâche'' et une ''instance de tâche'' : un même modèle de tâche peut être en effet instancié plusieurs fois dans une application. |
| 62 | * Un modèle de tâche est défini par la directive `TaskModel`. Il spécifie pour une tâche donnée ses ressources utilisées (canaux de communication, etc.) ainsi que ses implémentations existantes. Pour l'instant, on ne s'intéressera qu'aux implémentations logicielles déclarées par `SwTask`. Voir DsxTaskModel. |
| 63 | * Une instance de tâche est définie par la directive `Task`. Elle fait partie d'un TCG. Elle est connectée aux autres tâches par les ressources. Voir DsxTasks. |
105 | | Chaque tâche effectue un traitement élémentaire dans la décompression d'une image. |
106 | | Dans ce TCG, on représente les tâches par des ronds et les canaux de communication |
107 | | par des rectangles. Il s'agit donc d'un graphe bipartie. |
108 | | |
109 | | Notre animation MJPEG sera composée d'une séquence d'images ayant toutes la même taille. |
110 | | Comme une compression JPEG découpe l'image en blocs de 8x8 pixels, |
111 | | la hauteur et la largeur de chaque image sont multiples de 8. |
112 | | |
113 | | Nous allons utiliser les constantes suivantes: |
114 | | * WIDTH largeur de l'image en pixels |
115 | | * HEIGHT hauteur de l'image en pixels |
116 | | A partir de ces deux constantes, d'autres constantes sont définies dans le fichier `jpeg.h`: |
117 | | * BLOCKS_W nombre de blocs en largeur |
118 | | * BLOCKS_H nombre de blocs en hauteur |
119 | | * NBLOCKS nombre de blocs par image (= BLOCKS_W*BLOCKS_H) |
| 80 | Chaque tâche effectue un traitement élémentaire dans la décompression d'une image. Dans ce TCG, on représente les tâches par des ronds et les canaux de communication par des rectangles : il s'agit donc d'un graphe bipartie. |
| 81 | |
| 82 | Notre animation MJPEG sera composée d'une séquence d'images ayant toutes la même taille. Puisqu'une compression JPEG découpe l'image en blocs de 8x8 pixels, la hauteur et la largeur de chaque image sont multiples de 8. |
| 83 | |
| 84 | Afin que l'application soit générique (c'est-à-dire supporte différentes tailles d'images), elle utilise les constantes suivantes : |
| 85 | * `WIDTH` : largeur de l'image en pixels |
| 86 | * `HEIGHT` : hauteur de l'image en pixels |
| 87 | À partir de ces deux constantes, d'autres constantes sont définies : |
| 88 | * `BLOCKS_W` : nombre de blocs en largeur |
| 89 | * `BLOCKS_H` : nombre de blocs en hauteur |
| 90 | * `NBLOCKS` : nombre de blocs par image (= BLOCKS_W*BLOCKS_H) |
123 | | Il faut nommer chaque élément du TCG: |
124 | | * Les noms des tâches ont été définis en cours et sont imposés. |
125 | | * Vous pouvez choisir librement les noms des canaux de communication. |
126 | | Chaque canal de communication est attaché à au moins deux tâches par des ''ports''. |
127 | | On distingue le nom des canaux de communication et le nom des ports des tâches connectées à ces canaux. |
128 | | |
129 | | Pour déterminer les noms des ports des tâches, il est impératif de consulter |
130 | | le code des tâches fourni dans le fichier attachment:mjpeg_tp1.tar.bz2, |
131 | | recopiez ce fichier chez vous et décompressez-le. |
| 94 | Téléchargez le fichier attachment:mjpeg_tp1.tar.bz2 chez vous et décompressez-le : |
135 | | '''Note''': Même si le code sources des tâches {{{iqzz}}} et {{{libu}}} |
136 | | n'est pas fourni, vous pouvez connaître les largeurs de |
137 | | tous les canaux en vous référant au code des autres tâches, et le nom des |
138 | | ports est fourni dans la description DSX, dans les fichiers {{{src/*/*.task}}} et {{{mjpeg}}} |
139 | | (à compléter). |
140 | | |
141 | | Dans le fichier de description DSX {{{mjpeg}}}, |
142 | | * Pour chacun des modèles de tâches: |
143 | | * Reportez des noms pour chacun des ports d'entrée/sortie (cf DsxTasks). |
144 | | * Complétez la description des modèles de tâches (voir dans `src/*/*.task`) |
145 | | Iqzz, et Libu ont une déclaration particulière à ne pas prendre en compte pour l'instant, |
146 | | car ces modèles tâches sont fournis sans les sources: vous les écrirez aux prochaines questions |
147 | | * Pour chaque canal de communication: |
148 | | * Choisissez un nom et instanciez le canal |
149 | | (Nous avons 7 canaux Mwmr, utiliser l'API décrite dans DsxResource, `tg_demux` est fournie en exemple) |
150 | | * Dimensionnez (profondeur et largeur) les canaux en fonction des contraintes imposées par le code des tâches. |
151 | | * Créez un Tcg |
152 | | * en instanciant une tâche de chaque modèle, voir DsxTcg |
153 | | * en connectant les canaux aux ports des tâches, en les désignant par leurs noms |
| 98 | |
| 99 | La description DSX de l'application qui vous est fournie est incomplète, c'est à vous de remplir le code manquant. |
| 100 | |
| 101 | Commencez par compléter les modèles de tâches incomplets, qui se trouvent dans les fichiers {{{'src/*/*.task'}}}, en spécifiant les largeurs des canaux de communications manquantes. Pour trouver ces largeurs, vous devez consulter le code des tâches. |
| 102 | '''Note''': même si le code source des tâches {{{'iqzz'}}} et {{{'libu'}}} n'est pas fourni, vous pouvez connaître les largeurs des canaux en vous référant au code des autres tâches. |
| 103 | |
| 104 | Ensuite, vous devez compléter le fichier de description DSX `'mjpeg'` en plusieurs étapes : |
| 105 | |
| 106 | Tout d'abord, notez qu'il faut nommer chaque élément du TCG : |
| 107 | * Les noms des tâches ont été définis en cours et sont donc imposés. |
| 108 | * Vous pouvez par contre choisir librement les noms des canaux de communication. Chaque canal de communication est attaché à au moins deux tâches par des ''ports''. On distingue le nom des canaux de communication et le nom des ports des tâches connectées à ces canaux. Pour déterminer les noms des ports de chaque tâche, vous pouvez consulter le code des tâches ou bien le code des modèles de tâches. |
| 109 | |
| 110 | Puis, |
| 111 | * Pour chaque canal de communication Mwmr : |
| 112 | * Choisissez un nom et instanciez le canal. Il y a 7 canaux à définir, pour lesquels vous pouvez vous aider de l'API décrite dans DsxResource (`tg_demux` est fournie en exemple). |
| 113 | * Dimensionnez (largeur et profondeur) les canaux en fonction des contraintes imposées par le code des tâches. |
| 114 | * Créez un Tcg : |
| 115 | * Instanciez une tâche de chaque modèle. Voir DsxTcg. |
| 116 | * Connecter les canaux aux ports des tâches, en les désignant par leurs noms. |
184 | 146 | ||F,,56,,||F,,57,,||F,,58,,||F,,59,,||F,,60,,||F,,61,,||F,,62,,||F,,63,,|| |
185 | 147 | |
186 | | On applique sur ce bloc deux traitement successifs: |
187 | | |
188 | | * La quantisation inverse (IQ) est la multiplication de chaque élément d'entrée par un facteur |
189 | | de la table de 64 coefficients de quantisation inverse T,,n,,, globale pour l'image. |
| 148 | On applique sur ce bloc deux traitement successifs : |
| 149 | |
| 150 | * La quantisation inverse (IQ) est la multiplication de chaque élément d'entrée par un facteur de la table de 64 coefficients de quantisation inverse T,,n,,, globale pour l'image. |
216 | | * Pour implémenter ZZ, un tableau statique commençant par les |
217 | | valeurs ZZ![0]=0, ZZ![1]=1, ZZ![2]=8, ZZ![3]=16, ZZ![4]=9, ... |
218 | | vous sera probablement utile. |
219 | | * Les transformations IQ et ZZ doivent être implémentées dans |
220 | | la même boucle. |
221 | | * Les types des données sont: |
222 | | * T: Table de quantisation inverse (IQ): entiers non signés 8 bits |
223 | | * F,,n,,: Blocs en entrée: entiers 16 bits signés |
224 | | * F,,n,,': Blocs en sortie: entiers 32 bits signés (car 8bits*16bits nécessite au plus 24 bits...) |
225 | | * Votre code '''doit''' être portable quelle que soit l'endianness du processeur sous-jacent |
226 | | (si vous ne faites pas de transtypages hasardeux sur les pointeurs, ça devrait bien se passer) |
227 | | * Votre code '''doit''' gérer toutes les tailles d'images (tant qu'elles sont multiples de 8x8). |
228 | | Toutes les boucles doivent utiliser les tailles issues des constantes (WIDTH, HEIGHT, BLOCKS_W, BLOCKS_H) |
| 177 | * Pour implémenter ZZ, un tableau statique commençant par les valeurs ZZ![0]=0, ZZ![1]=1, ZZ![2]=8, ZZ![3]=16, ZZ![4]=9, etc. vous sera probablement utile. |
| 178 | * Les transformations IQ et ZZ doivent être implémentées dans la même boucle de code. |
| 179 | * Les types des données sont : |
| 180 | * T : Table de quantisation inverse (IQ) : entiers non signés 8 bits |
| 181 | * F,,n,, : Blocs en entrée : entiers 16 bits signés |
| 182 | * F,,n,,' : Blocs en sortie : entiers 32 bits signés (car 8bits*16bits nécessite au plus 24 bits...) |
| 183 | * Votre code '''doit''' être portable quelle que soit l'endianness du processeur sous-jacent (si vous ne faites pas de transtypages hasardeux sur les pointeurs, ça devrait bien se passer). |
| 184 | * Votre code '''doit''' gérer toutes les tailles d'images (tant qu'elles sont multiples de 8x8). Cela signifie que toutes les boucles doivent utiliser les tailles issues des constantes (`WIDTH`, `HEIGHT`, `BLOCKS_W`, `BLOCKS_H`). |
240 | | Inspirez-vous des autres déclarations, n'oubliez pas les `defines` si vous voulez un code portable. |
241 | | |
242 | | En fonction de la définition d'{{{iqzz}}} que vous utilisez (celle en `.bc` ou la vôtre en `.c`), |
243 | | et en recompilant, vous observerez les résultats l'implémentation de référence ou de la vôtre. |
244 | | |
245 | | * Affinez votre fonction. Si besoin, lancez l'application {{{exe.posix}}} dans un débugger. |
246 | | La fonction implémentant {{{iqzz}}} portera probablement le nom {{{iqzz_func_iqzz}}}. |
| 196 | |
| 197 | En fonction de la définition d'{{{iqzz}}} que vous utilisez (celle en `.bc` ou la vôtre en `.c`), et en recompilant, vous pouvez observer les différences entre l'implémentation de référence et la vôtre. |
| 198 | |
| 199 | * Affinez votre fonction. Si besoin, lancez l'application {{{exe.posix}}} dans un débugger (`'gdb'`) : dans ce cas, notez que la fonction implémentant {{{iqzz}}} portera probablement le nom {{{iqzz_func_iqzz}}}. |
250 | | Un Ramdac est une RAM couplée à un DAC (Digital to Analog Converter). Le contenu de la ram est |
251 | | converti en signal analogique pour être envoyé sur un écran. Notre Ramdac a un accès particulier: |
252 | | Il a un comportement Fifo. Il faut écrire les pixels dans l'ordre où ils vont être affichés : |
253 | | tous les pixels d'une ligne, puis toutes les lignes d'une image. |
254 | | |
255 | | Il se trouve que les blocs issus de la décompression JPEG font 8x8 pixels. Ils ne font pas la |
256 | | largeur de l'image, il faut donc construire des lignes d'image à partir des blocs issus de la décompression. |
257 | | C'est le but de la tâche Libu (Line Builder). |
258 | | |
259 | | Libu prend BLOCKS_W blocs de 8x8 pixels et en construit 8 lignes de WIDTH pixels de large |
260 | | (rappel: BLOCKS_W*8 = WIDTH). Il peut alors envoyer successivement ces lignes au Ramdac. |
261 | | |
262 | | En pseudo-code, le traitement de Libu est: |
| 203 | Un ''Ramdac'' est une RAM couplée à un DAC (Digital to Analog Converter). Le contenu de la ram est converti en signal analogique pour être envoyé sur un écran. Le Ramdac que nous utilisons ici possède un accès particulier : il se comporte comme une FIFO. Il faut écrire les pixels dans l'ordre où ils vont être affichés : tous les pixels d'une ligne, puis toutes les lignes d'une image. |
| 204 | |
| 205 | Il se trouve que les blocs issus de la décompression JPEG ont une taille de 8x8 pixels. Ils n'occupent donc pas la largeur de l'image, et il faut alors construire des lignes d'image à partir des blocs issus de la décompression. C'est le but de la tâche ''Libu'' (Line Builder). |
| 206 | |
| 207 | Libu récupère `BLOCKS_W` blocs de 8x8 pixels et s'en sert pour construire 8 lignes de `WIDTH` pixels de large (rappel: BLOCKS_W*8 = WIDTH). Libu peut alors envoyer successivement ces lignes au Ramdac. |
| 208 | |
| 209 | En pseudo-code, le traitement exécuté par Libu est : |
281 | | Vous devrez créer une archive `tar.gz`, contenant un seul répertoire nommé `tp1`. Dans ce répertoire vous devrez mettre: |
282 | | |
283 | | * le fichier `mjpeg`, contenant la description de l'application |
284 | | * Les répertoires des tâches iqzz et libu, contenant uniquement les fichier `.task` et `.c` (pas le `.bc`) |
285 | | Bien entendu, ces deux descriptions de tâches doivent faire référence au .c, pas au .bc !) |
286 | | * Votre rapport (une page maximum) en format PDF (et aucun autre) dans un fichier nommé '''exactement''' `tp1/rapport.pdf`. |
287 | | |
288 | | Le nom de fichier de l'archive doit contenir les nom des deux auteurs, séparés par un ''underscore'' (_), |
289 | | par exemple: `dupond_dupont.tar.gz`. |
290 | | |
291 | | Faites particulièrement attention à cette archive. elle fera l'objet d'une correction automatique pour la |
292 | | validation des sources, d'où le format strict. |
293 | | |
294 | | Pour être surs de vous, le listing du contenu de l'archive doit donner cette liste avec ces noms, |
295 | | et rien de plus (l'ordre des fichiers n'importe pas). La commande suivante doit donner exactement cette sortie: |
| 228 | Vous devrez créer une archive `.tar.gz`, contenant un seul répertoire nommé `tp1`. Dans ce répertoire vous devrez mettre (uniquement) : |
| 229 | |
| 230 | * le fichier `mjpeg`, contenant la description de l'application. |
| 231 | * Les répertoires des tâches `iqzz` et `libu`, contenant uniquement les fichier `.task` et `.c` (pas le `.bc`). Bien entendu, ces deux descriptions de tâches `.task` doivent faire référence au `.c`, pas au `.bc`. |
| 232 | * Votre rapport (une page maximum) qui répond aux questions posées dans le sujet de TP, doit être au format PDF (et aucun autre) dans un fichier nommé '''exactement''' `rapport.pdf`. |
| 233 | |
| 234 | Le nom de fichier de l'archive doit contenir les nom des deux auteurs, séparés par un ''underscore'' (_) (par exemple: `dupond_dupont.tar.gz`). |
| 235 | |
| 236 | Faites particulièrement attention à cette archive : elle fera l'objet d'une correction automatique pour la validation des sources, d'où le format strict. |
| 237 | |
| 238 | Pour être sur de vous, l'exécution de la commande suivante sur votre archive doit donner exactement la même sortie : |