wiki:TP1_Station

TP1 : Description de l'application MJPEG avec DSX et exécution sur station de travail

0. Objectif

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.

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 :

  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). On suppose que le parallélisme "gros grain" de l'application ainsi que le schéma des communication entre les tâches peuvent être statiquement définis par le concepteur et n'évoluent pas en cours d'exécution.
  2. 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.
  3. 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 objets logiciels (canaux de communication, piles d'exécution des tâches) dans les bancs mémoire.

L'exécution de cette description DSX/L permet générer trois choses :

  1. Une version de l'application logicielle multi-tâches compatible POSIX, pouvant donc être 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.
  2. 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 générer un simulateur de cette architecture.
  3. Le code binaire de l'application logicielle embarquée, obtenu en cross-compilant cette application pour le ou les processeur(s) présent(s) sur le MPSoC, et en effectuant l'édition de liens avec le système d'exploitation embarqué. Ce code binaire exécutable peut être directement chargé dans la mémoire du MP-SoC juste avant le lancement de la simulation.

Dans ce 1er 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 enfin à valider cette application en l'exécutant sur une station de travail Linux.

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. Votre rapport devra contenir les réponses aux questions posées dans le sujet, visuellement signalées en gras oblique.

1. Prise en main

1.1. Exécuter l'application SplitMsg

Pour prendre en main l'outil DSX, on s'intéresse à une application parallèle extrêmement simple comportant deux tâches et un seul canal de communication MWMR. Cette application s'appelle SplitMsg.

  • Importez l'environnement nécessaire dans le contexte de votre shell
    $ source /users/outil/dsx/dsx_env.sh
    
  • Créez un répertoire 'tp1' puis un sous-répertoire 'splitmsg' dans lequel vous mettrez les fichiers à recopier.
    • Pour les fichiers, voir la page SplitMsg
  • Si ce n'est pas déjà fait, rendez la description DSX exécutable :
    $ chmod +x fichier_de_description
    
  • Exécutez la description DSX/L qui réalise la compilation l'application logicielle générée par DSX :
    $ ./fichier_de_description
    
  • Question 1 : Quels fichiers étaient présents avant cette commande ? Quels fichiers ou répertoires ont été créés ?
  • Lancez le programme multi-tâches POSIX généré qui porte le nom 'exe.posix' (vous pourrez interrompre l'exécution à tout moment en pressant Ctrl-c) :
    $ ./exe.posix
    
  • Question 2 : Comment interpréter ce que vous observez lors de l'exécution de cette application ?

1.2. Anatomie de la description DSX/L

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.

  • 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 dsx:wiki:DsxTaskModel.
  • 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 dsx:wiki:DsxTasks.

Dans les applications décrites dans ce TP, chaque modèle de tâche ne sera utilisé qu'une fois.

La description DSX/L de l'application SplitMsg comprends deux parties.

  • Question 3 : Rappelez à quoi sert chacune des 2 parties ?
  • Question 4 : Qu'est-ce qui n'est pas présent dans la description DSX/L, mais qui est quand même indispensable à la construction et à l'exécution de l'application ?

2. Application MJPEG

Dans le reste du TP, on s'intéresse à l'application MJPEG telle que décrite en cours. On rappelle la structure du Graphe des Tâches et des Communications :

MJPEG TCG

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.

Notre animation MJPEG est 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.

Afin que l'application soit générique (c'est-à-dire supporte différentes tailles d'images), elle utilise les constantes suivantes :

  • WIDTH : largeur de l'image en pixels
  • HEIGHT : hauteur de l'image en pixels

À partir de ces deux constantes, d'autres constantes sont définies :

  • BLOCKS_W : nombre de blocs en largeur
  • BLOCKS_H : nombre de blocs en hauteur
  • NBLOCKS : nombre de blocs par image (= BLOCKS_W*BLOCKS_H)

2.1. Spécifier le TCG

Téléchargez le fichier attachment:mjpeg.tar.bz2 chez vous et décompressez-le dans le répertoire tp1 :

~/sesi-mjpeg/tp1 $ tar xjvf mjpeg.tar.bz2

La description DSX de l'application qui vous est fournie est incomplète, c'est à vous de remplir le code manquant.

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.

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.

Ensuite, vous devez compléter le fichier de description DSX 'mjpeg' en plusieurs étapes.

Tout d'abord, notez qu'il faut nommer chaque élément du TCG :

  • Les noms des tâches ont été définis en cours et sont donc imposés.
  • 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.

Puis,

  • Pour chaque canal de communication Mwmr :
    • 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 dsx:wiki:DsxResource (tg_demux est fournie en exemple).
    • Dimensionnez (largeur et profondeur) les canaux en fonction des contraintes imposées par le code des tâches.
  • Créez un Tcg :
    • Instanciez une tâche de chaque modèle. Voir dsx:wiki:DsxTcg.
    • Connecter les canaux aux ports des tâches, en les désignant par leurs noms.

2.2. Exécution de l'application

  • Exécutez le fichier de description, qui réalise la compilation :
    $ ./mjpeg
    
  • Lancez l'exécution de l'application :
    $ ./exe.posix
    
  • Question 5 : Décrivez brièvement ce que vous observez

2.3. Écriture en C de la tâche IQZZ

IQZZ est une tâche réalisant un double traitement, appliqué successivement à chaque bloc de 8x8 pixels de l'image.

  • La quantisation inverse (IQ) est la multiplication de chaque élément d'un bloc par un facteur dépendant de sa position dans le bloc.

La täche IQZZ utilise un tableau à deux dimensions contenant les 64 coefficents de quantisation inverse. On utilise les mêmes coefficients pour tous les blocs d'une même image. Les valeurs de ces coefficients sont donc chargés dans la pile d'exécution de la tâche IQZZ au début de traitement de chaque image à travers un canal MWMR dédié, en provenance de la tâche DEMUX.

Un bloc entrant dans IQZZ est composé de 8x8=64 facteurs.

F0F1F2F3F4F5F6F7
F8F9F10F11F12F13F14F15
F16F17F18F19F20F21F22F23
F24F25F26F27F28F29F30F31
F32F33F34F35F36F37F38F39
F40F41F42F43F44F45F46F47
F48F49F50F51F52F53F54F55
F56F57F58F59F60F61F62F63

Fn' = Fn * Tn

F0'F1'F2'F3'F4'F5'F6'F7'
F8'F9'F10'F11'F12'F13'F14'F15'
F16'F17'F18'F19'F20'F21'F22'F23'
F24'F25'F26'F27'F28'F29'F30'F31'
F32'F33'F34'F35'F36'F37'F38'F39'
F40'F41'F42'F43'F44'F45'F46'F47'
F48'F49'F50'F51'F52'F53'F54'F55'
F56'F57'F58'F59'F60'F61'F62'F63'
  • La transformation ZigZag (ZZ) est un réordonnancement des pixels.

Après le réordonnancement, l'ordre des facteurs en sortie doit être:

F0'F1'F5'F6'F14'F15'F27'F28'
F2'F4'F7'F13'F16'F26'F29'F42'
F3'F8'F12'F17'F25'F30'F41'F43'
F9'F11'F18'F24'F31'F40'F44'F53'
F10'F19'F23'F32'F39'F45'F52'F54'
F20'F22'F33'F38'F46'F51'F55'F60'
F21'F34'F37'F47'F50'F56'F59'F61'
F35'F36'F48'F49'F57'F58'F62'F63'

Notes d'implémentation :

  • 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.
  • Les transformations IQ et ZZ doivent être implémentées dans la même boucle de code.
  • Les types des données sont :
    • T : Table de quantisation Q (entiers non signés 8 bits)
    • Fn : Blocs en entrée (entiers 16 bits signés)
    • Fn' : Blocs en sortie (entiers 32 bits signés, car 8bits*16bits nécessite au plus 24 bits...)
  • 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).
  • 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 et NBLOCKS), définies dans le fichier jpeg.h.

Instructions:

  • Écrivez en C le code de la tâche IQZZ à l'aide de l'API logicielle définie dans dsx:wiki:SrlApi (inspirez vous également du code des autres tâches).
  • Changez la définition du modèle de la tâche IQZZ dans la description DSX pour prendre en compte cette nouvelle implémentation :
    # On avait dans iqzz.task:
    sources = [ 'iqzz.bc' ],
    
    # On peut alors déclarer iqzz comme une tâche logicielle en C.
    sources = [ 'iqzz.c' ],
    

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.

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

2.4. Écriture en C de la tâche LIBU

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.

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

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.

En pseudo-code, le traitement exécuté par Libu est :

bloc : 8x8 pixels
buffer : WIDTH*8 pixels

Pour chaque 0 .. BLOCKS_H:
    Pour chaque 0 .. BLOCKS_W:
        Lire un bloc
        Pour chaque ligne du bloc
            Copier les 8 pixels en les mettant à leur place dans buffer
    Envoyer les 8 lignes qui sont dans le buffer
  • Implémentez cette tâche en C à l'aide de l'API logicielle définie dans dsx:wiki:SrlApi.
  • Modifiez la description de l'application DSX pour prendre en compte votre source.
  • Testez l'application (après recompilation).

3. Compte-Rendu

Vous devrez créer une archive .tar.gz, contenant un seul répertoire nommé tp1. Dans ce répertoire vous devrez mettre (uniquement) :

  • le fichier mjpeg, contenant la description de l'application.
  • 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.
  • 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.

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

Faites particulièrement attention à cette archive : elle fera l'objet d'une correction automatique pour la validation des sources, d'où le format strict.

Pour être sûr de vous, l'exécution de la commande suivante sur votre archive doit vous rendre exactement la sortie correspondante :

$ tar tzf nombinome0_nombinome1.tar.gz
tp1/
tp1/mjpeg/
tp1/mjpeg/mjpeg
tp1/mjpeg/src/
tp1/mjpeg/src/iqzz
tp1/mjpeg/src/iqzz/iqzz.c
tp1/mjpeg/src/iqzz/iqzz.task
tp1/mjpeg/src/libu/
tp1/mjpeg/src/libu/libu.c
tp1/mjpeg/src/libu/libu.task
tp1/rapport.pdf
$

Envoyez cette archive au plus tard la veille de la séance du TP 2, à [MailAsim:Quentin.Meunier Quentin Meunier]. Tout retard sera sanctionné.

Suite

TP Suivant: TP2_Monopro

Last modified 7 years ago Last modified on Dec 13, 2016, 5:17:29 PM

Attachments (2)

Download all attachments as: .zip